Skip to content

dedupe: get deps from shrinkwrap - #118

Closed
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12
Closed

dedupe: get deps from shrinkwrap#118
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12

Conversation

@larsgw

Copy link
Copy Markdown
Contributor

@larsgw
larsgw requested a review from a team as a code ownerDecember 13, 2018 22:35
@larsgw

Copy link
Copy Markdown
ContributorAuthor

(Breaking) side effects include:

  • installing any package that is in package-lock.json but not in node_modules

It doesn't install packages that are only specified in package.json.

@zkatzkat added the semver:major backwards-incompatible breaking changes label Jan 7, 2019
@aeschright

Copy link
Copy Markdown
Contributor

We're going to make sure this doesn't have any unintended consequences and goes in the right direction.

@aeschrightaeschright left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'd like you to redo the test using our standard test script: see scripts/maketest -- you can give it a directory fixture and it will give you the test setup. It'll help us keep things consistent and avoid some older conventions that aren't effective.

@larsgw

larsgw commented Jan 16, 2019

Copy link
Copy Markdown
ContributorAuthor

I added the tests, and a doc change now too. I think this change is going to be pretty controversial, seeing how many people already disagree with npm install behavior, fixing incorrect package-lock.json files. The only middle way I can think of, fixing the bug and keeping the behavior, would be to somehow merge all movements in the node_modules tree with all the modules left behind in the shrinkwrap tree, but I don't know if that's feasible.

@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 6ca2f43 to 6d0cc95CompareJanuary 18, 2019 19:17
@zkat
zkatforce-pushed the release-next branch 3 times, most recently from db63b89 to b09bc8cCompareJanuary 23, 2019 18:36
@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 06cdf5b to f957798CompareFebruary 20, 2019 20:42
@G-Rath

G-Rath commented May 14, 2019

Copy link
Copy Markdown
Contributor

What's the status of this? The community issue was closed, so I can't ask on that.

For me as a Windows user, with my expectation of what ddp does, I find it odd that running ddp results in a package-lock.json that will change if I then run npm i.

The core question I have is: Why is npm i able to generate a package-lock.json that includes fsevents without actually installing that package?

@FranklinYu

Copy link
Copy Markdown

@G-Rath I don’t think that’s an issue. Optional dependency should also be locked, so that when the package is installed on another platform, it won’t change the lock file; this way the lock file converges. In contrary, it doesn’t make sense to remove fsevents when it doesn’t apply to current platform.

@G-Rath

Copy link
Copy Markdown
Contributor

@FranklinYu For sure.

My question wasn't a challenge at npm i, but as a starting point to try and figure out how ddp should act, as this PR has the breaking side effect of :

installing any package that is in package-lock.json but not in node_modules

The point I was wanting to draw attention to with my question is that npm idoesn't have to install all packages to have them in the package-lock.json, and so why does ddp have to?

While I'm sure there's a good reason, in my eyes that highlights a starting point for identifying where ddp drifts away from i.

@FranklinYu

Copy link
Copy Markdown

installing any package that is in package-lock.json but not in node_modules

I think that only refers to packages that work for current platform. For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

@G-Rath

G-Rath commented Jun 10, 2019

Copy link
Copy Markdown
Contributor

For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Currently, if I run npm i on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get the same lock file regardless of OS.

Currently, if I run npm ddp on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get a lock file that will differ based on what packages are supported by that OS.

Example:

Given this package.json:

{
"optionalDependencies": {
"fsevents": "^2.0.7"
}
}

Running npm i on Windows, Linux, & OSX gives you the following lock:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Running npm ddp on Windows or Linux gives you:

{
"lockfileVersion": 1
}

Running npm ddp on Mac gives you:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Note that nothing in node_modules has changed - the only change happening here is in the package-lock.json (aside from npm i on OSX, which of course installs fsevents)

@FranklinYu

Copy link
Copy Markdown

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Exactly. The whole point of this PR is that dedupeshould not remove fsevents.

@G-Rath

Copy link
Copy Markdown
Contributor

The whole point of this PR is that dedupe should not remove fsevents.

Exactly. I've never said otherwise; but it's good we're on the same page 🙂

My question is about the side effect:

installing any package that is in package-lock.json but not in node_modules

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From my understanding, the worst-case bare minimal solution would be to simply run npm i after npm ddp. That is of course highly inefficient, but it's a starting point - from there you can look to strip away unneeded parts from npm i until you're left w/ the minimal code required to "fix" the side effect.

That's a "fix it in post" style approach - which isn't bad; those kind of fixes are often just far more simpler & maintainable than the alternative: try to "fix" the actual root cause of the problem.

This could just be b/c I'm misunderstanding what is meant by "installing any package that is in package-lock.json but not in node_modules", or that just due to the complex nature of npm, that's just how it is, but as I've said: there is at least one way to work around it, that I don't see any major downsides to, given that it seems to give what we all agree is the least astonishing result from running such a command.

Eitherway, I'm just generally interested in understanding more about npm works that means that this is a side effect (w/o spending hours taking it apart line by line*) 😄

*: I mean, I'd love to do this, but sadly don't have the time 😂

@larsgw

Copy link
Copy Markdown
ContributorAuthor

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From what I remember (it's been a while), npm dedupe works like this:

  1. Read node_modules/ tree (like npm ls, and not package-lock.json like npm i)
  2. Perform deduping on the tree
  3. Write the tree to node_modules/ and package-lock.json

Since non-installed optional dependencies have no record in the node_modules/ tree they aren't included in the new package-lock.json. To fix that, I see two possibilities:

  1. Read from package-lock.json instead of node_modules. This has the added side-effect of installing (or trying to install) any packages that are in the package lock, but not in node_modules. This is because only reading the package lock can't tell you certain packages shouldn't be installed, just like only reading node_modules can't tell you if certain packages should be installed. However, that seemed like a smaller problem to me.
  2. What I mentioned above: somehow track the movements of the deduping process and merge those with the package lock, but leave the rest of the process. Way above my pay-grade as a voluntary contributor (and please don't spend time doing that yourself either).

PS: I think I thought of a third option just now, but I can't think of it anymore so I'll come back to this if the thought does.

@G-Rath

Copy link
Copy Markdown
Contributor

Ah interesting - thanks for the explanation :)

Would it be viable to try and leverage the logic that npm i uses to determine if a package should be installed or not?

That could be going in the direction you said not spend time trying to do via 2. :)

I'm also wondering if there is any to know (or get an idea of) all the possible conditions that can result in a package not being installed but being written to package-lock.json.

So far I only know of optionalDependencies, which from what happens w/ fsevents, seem to always be included in the package-lock.json, and so wouldn't be something ddp has to worry about?

I feel like this strengthens the argument that ddp should optimise based on package-lock.json: while it's annoying, if you're going to install a dependency in some situations, you should optimise for that situation, since otherwise isn't it harder/more work to safely determine what packages should be installed?

This has the added side-effect of installing (or trying to install)

To me that sounds like it'll work, depending on how the "trying" is handled; Looking at the error output from doing npm i fsevents on Windows, it's throwing EBADPLATFORM - That to me seems like an exception that'd only really be thrown in a predictable situation (unlike say EBADPERMISSIONS^).

Which I think boils back down to the first part of this comment, about how does npm i determine if a package should be installed, and can that be leveraged here?

*: Luckly, fsevents has no dependencies, and from what I've seen, it's the most common optionalDependencies out there.
^: Might not be an actual exception, but you know what I mean

@FranklinYu

Copy link
Copy Markdown

I like option 1. Package lock file should be the source of truth. Everyone knows that node_modules is transient and nobody checks it into version control.

@G-Rath

G-Rath commented Jun 11, 2019

Copy link
Copy Markdown
Contributor

Plus if you do "need" to modify anything in node_modules, you can use patch-package to do so stably.

@FranklinYu

Copy link
Copy Markdown

sane seems to have switched from fsevents package to fs.watch(). For future testers, we have other examples:

Both depend on fsevents for now.

@jpsfs

jpsfs commented May 6, 2020

Copy link
Copy Markdown

@FranklinYu any update on this?

@FranklinYu

Copy link
Copy Markdown

@jpsfs I think you’re mentioning the wrong user. I’m not part of npm team so I can’t merge this even though I want this feature. I haven’t see any npm member involved in discussion by far, so I wouldn’t expect this to happen any time soon.

@darcyclarke

Copy link
Copy Markdown
Contributor

@larsgw sorry for the delay here. We're going to close this as much of this code has been moved to Arborist (dedupe has been rewritten in v7)

@FranklinYu

Copy link
Copy Markdown

@darcyclarke Did you imply that NPM v7 won’t have this issue any more?

@TimDaubTimDaub mentioned this pull request Mar 15, 2022
2 tasks
antongolub pushed a commit to antongolub-forks/npm-cli that referenced this pull request May 18, 2024
🤖 I have created a release *beep* *boop*
---
## [4.0.4](npm/bin-links@v4.0.3...v4.0.4)
(2024-05-04)
### Bug Fixes
*
[`100a4b7`](npm/bin-links@100a4b7)
[npm#117](npm/bin-links#117) linting:
no-unused-vars (@lukekarrys)
### Chores
*
[`e955437`](npm/bin-links@e955437)
[npm#117](npm/bin-links#117) bump
@npmcli/template-oss to 4.22.0 (@lukekarrys)
*
[`b602aca`](npm/bin-links@b602aca)
[npm#117](npm/bin-links#117) postinstall for
dependabot template-oss PR (@lukekarrys)
*
[`955cc34`](npm/bin-links@955cc34)
[npm#116](npm/bin-links#116) bump
@npmcli/template-oss from 4.21.3 to 4.21.4 (@dependabot[bot])
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 2, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 13, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:majorbackwards-incompatible breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@larsgw@aeschright@G-Rath@FranklinYu@jpsfs@darcyclarke@zkat
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
dedupe: get deps from shrinkwrap by larsgw · Pull Request #118 · npm/cli · GitHub
Skip to content

dedupe: get deps from shrinkwrap - #118

Closed
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12
Closed

dedupe: get deps from shrinkwrap#118
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12

Conversation

@larsgw

Copy link
Copy Markdown
Contributor

@larsgw
larsgw requested a review from a team as a code ownerDecember 13, 2018 22:35
@larsgw

Copy link
Copy Markdown
ContributorAuthor

(Breaking) side effects include:

  • installing any package that is in package-lock.json but not in node_modules

It doesn't install packages that are only specified in package.json.

@zkatzkat added the semver:major backwards-incompatible breaking changes label Jan 7, 2019
@aeschright

Copy link
Copy Markdown
Contributor

We're going to make sure this doesn't have any unintended consequences and goes in the right direction.

@aeschrightaeschright left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'd like you to redo the test using our standard test script: see scripts/maketest -- you can give it a directory fixture and it will give you the test setup. It'll help us keep things consistent and avoid some older conventions that aren't effective.

@larsgw

larsgw commented Jan 16, 2019

Copy link
Copy Markdown
ContributorAuthor

I added the tests, and a doc change now too. I think this change is going to be pretty controversial, seeing how many people already disagree with npm install behavior, fixing incorrect package-lock.json files. The only middle way I can think of, fixing the bug and keeping the behavior, would be to somehow merge all movements in the node_modules tree with all the modules left behind in the shrinkwrap tree, but I don't know if that's feasible.

@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 6ca2f43 to 6d0cc95CompareJanuary 18, 2019 19:17
@zkat
zkatforce-pushed the release-next branch 3 times, most recently from db63b89 to b09bc8cCompareJanuary 23, 2019 18:36
@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 06cdf5b to f957798CompareFebruary 20, 2019 20:42
@G-Rath

G-Rath commented May 14, 2019

Copy link
Copy Markdown
Contributor

What's the status of this? The community issue was closed, so I can't ask on that.

For me as a Windows user, with my expectation of what ddp does, I find it odd that running ddp results in a package-lock.json that will change if I then run npm i.

The core question I have is: Why is npm i able to generate a package-lock.json that includes fsevents without actually installing that package?

@FranklinYu

Copy link
Copy Markdown

@G-Rath I don’t think that’s an issue. Optional dependency should also be locked, so that when the package is installed on another platform, it won’t change the lock file; this way the lock file converges. In contrary, it doesn’t make sense to remove fsevents when it doesn’t apply to current platform.

@G-Rath

Copy link
Copy Markdown
Contributor

@FranklinYu For sure.

My question wasn't a challenge at npm i, but as a starting point to try and figure out how ddp should act, as this PR has the breaking side effect of :

installing any package that is in package-lock.json but not in node_modules

The point I was wanting to draw attention to with my question is that npm idoesn't have to install all packages to have them in the package-lock.json, and so why does ddp have to?

While I'm sure there's a good reason, in my eyes that highlights a starting point for identifying where ddp drifts away from i.

@FranklinYu

Copy link
Copy Markdown

installing any package that is in package-lock.json but not in node_modules

I think that only refers to packages that work for current platform. For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

@G-Rath

G-Rath commented Jun 10, 2019

Copy link
Copy Markdown
Contributor

For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Currently, if I run npm i on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get the same lock file regardless of OS.

Currently, if I run npm ddp on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get a lock file that will differ based on what packages are supported by that OS.

Example:

Given this package.json:

{
"optionalDependencies": {
"fsevents": "^2.0.7"
}
}

Running npm i on Windows, Linux, & OSX gives you the following lock:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Running npm ddp on Windows or Linux gives you:

{
"lockfileVersion": 1
}

Running npm ddp on Mac gives you:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Note that nothing in node_modules has changed - the only change happening here is in the package-lock.json (aside from npm i on OSX, which of course installs fsevents)

@FranklinYu

Copy link
Copy Markdown

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Exactly. The whole point of this PR is that dedupeshould not remove fsevents.

@G-Rath

Copy link
Copy Markdown
Contributor

The whole point of this PR is that dedupe should not remove fsevents.

Exactly. I've never said otherwise; but it's good we're on the same page 🙂

My question is about the side effect:

installing any package that is in package-lock.json but not in node_modules

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From my understanding, the worst-case bare minimal solution would be to simply run npm i after npm ddp. That is of course highly inefficient, but it's a starting point - from there you can look to strip away unneeded parts from npm i until you're left w/ the minimal code required to "fix" the side effect.

That's a "fix it in post" style approach - which isn't bad; those kind of fixes are often just far more simpler & maintainable than the alternative: try to "fix" the actual root cause of the problem.

This could just be b/c I'm misunderstanding what is meant by "installing any package that is in package-lock.json but not in node_modules", or that just due to the complex nature of npm, that's just how it is, but as I've said: there is at least one way to work around it, that I don't see any major downsides to, given that it seems to give what we all agree is the least astonishing result from running such a command.

Eitherway, I'm just generally interested in understanding more about npm works that means that this is a side effect (w/o spending hours taking it apart line by line*) 😄

*: I mean, I'd love to do this, but sadly don't have the time 😂

@larsgw

Copy link
Copy Markdown
ContributorAuthor

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From what I remember (it's been a while), npm dedupe works like this:

  1. Read node_modules/ tree (like npm ls, and not package-lock.json like npm i)
  2. Perform deduping on the tree
  3. Write the tree to node_modules/ and package-lock.json

Since non-installed optional dependencies have no record in the node_modules/ tree they aren't included in the new package-lock.json. To fix that, I see two possibilities:

  1. Read from package-lock.json instead of node_modules. This has the added side-effect of installing (or trying to install) any packages that are in the package lock, but not in node_modules. This is because only reading the package lock can't tell you certain packages shouldn't be installed, just like only reading node_modules can't tell you if certain packages should be installed. However, that seemed like a smaller problem to me.
  2. What I mentioned above: somehow track the movements of the deduping process and merge those with the package lock, but leave the rest of the process. Way above my pay-grade as a voluntary contributor (and please don't spend time doing that yourself either).

PS: I think I thought of a third option just now, but I can't think of it anymore so I'll come back to this if the thought does.

@G-Rath

Copy link
Copy Markdown
Contributor

Ah interesting - thanks for the explanation :)

Would it be viable to try and leverage the logic that npm i uses to determine if a package should be installed or not?

That could be going in the direction you said not spend time trying to do via 2. :)

I'm also wondering if there is any to know (or get an idea of) all the possible conditions that can result in a package not being installed but being written to package-lock.json.

So far I only know of optionalDependencies, which from what happens w/ fsevents, seem to always be included in the package-lock.json, and so wouldn't be something ddp has to worry about?

I feel like this strengthens the argument that ddp should optimise based on package-lock.json: while it's annoying, if you're going to install a dependency in some situations, you should optimise for that situation, since otherwise isn't it harder/more work to safely determine what packages should be installed?

This has the added side-effect of installing (or trying to install)

To me that sounds like it'll work, depending on how the "trying" is handled; Looking at the error output from doing npm i fsevents on Windows, it's throwing EBADPLATFORM - That to me seems like an exception that'd only really be thrown in a predictable situation (unlike say EBADPERMISSIONS^).

Which I think boils back down to the first part of this comment, about how does npm i determine if a package should be installed, and can that be leveraged here?

*: Luckly, fsevents has no dependencies, and from what I've seen, it's the most common optionalDependencies out there.
^: Might not be an actual exception, but you know what I mean

@FranklinYu

Copy link
Copy Markdown

I like option 1. Package lock file should be the source of truth. Everyone knows that node_modules is transient and nobody checks it into version control.

@G-Rath

G-Rath commented Jun 11, 2019

Copy link
Copy Markdown
Contributor

Plus if you do "need" to modify anything in node_modules, you can use patch-package to do so stably.

@FranklinYu

Copy link
Copy Markdown

sane seems to have switched from fsevents package to fs.watch(). For future testers, we have other examples:

Both depend on fsevents for now.

@jpsfs

jpsfs commented May 6, 2020

Copy link
Copy Markdown

@FranklinYu any update on this?

@FranklinYu

Copy link
Copy Markdown

@jpsfs I think you’re mentioning the wrong user. I’m not part of npm team so I can’t merge this even though I want this feature. I haven’t see any npm member involved in discussion by far, so I wouldn’t expect this to happen any time soon.

@darcyclarke

Copy link
Copy Markdown
Contributor

@larsgw sorry for the delay here. We're going to close this as much of this code has been moved to Arborist (dedupe has been rewritten in v7)

@FranklinYu

Copy link
Copy Markdown

@darcyclarke Did you imply that NPM v7 won’t have this issue any more?

@TimDaubTimDaub mentioned this pull request Mar 15, 2022
2 tasks
antongolub pushed a commit to antongolub-forks/npm-cli that referenced this pull request May 18, 2024
🤖 I have created a release *beep* *boop*
---
## [4.0.4](npm/bin-links@v4.0.3...v4.0.4)
(2024-05-04)
### Bug Fixes
*
[`100a4b7`](npm/bin-links@100a4b7)
[npm#117](npm/bin-links#117) linting:
no-unused-vars (@lukekarrys)
### Chores
*
[`e955437`](npm/bin-links@e955437)
[npm#117](npm/bin-links#117) bump
@npmcli/template-oss to 4.22.0 (@lukekarrys)
*
[`b602aca`](npm/bin-links@b602aca)
[npm#117](npm/bin-links#117) postinstall for
dependabot template-oss PR (@lukekarrys)
*
[`955cc34`](npm/bin-links@955cc34)
[npm#116](npm/bin-links#116) bump
@npmcli/template-oss from 4.21.3 to 4.21.4 (@dependabot[bot])
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 2, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 13, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:majorbackwards-incompatible breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@larsgw@aeschright@G-Rath@FranklinYu@jpsfs@darcyclarke@zkat
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' dedupe: get deps from shrinkwrap by larsgw · Pull Request #118 · npm/cli · GitHub
Skip to content

dedupe: get deps from shrinkwrap - #118

Closed
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12
Closed

dedupe: get deps from shrinkwrap#118
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12

Conversation

@larsgw

Copy link
Copy Markdown
Contributor

@larsgw
larsgw requested a review from a team as a code ownerDecember 13, 2018 22:35
@larsgw

Copy link
Copy Markdown
ContributorAuthor

(Breaking) side effects include:

  • installing any package that is in package-lock.json but not in node_modules

It doesn't install packages that are only specified in package.json.

@zkatzkat added the semver:major backwards-incompatible breaking changes label Jan 7, 2019
@aeschright

Copy link
Copy Markdown
Contributor

We're going to make sure this doesn't have any unintended consequences and goes in the right direction.

@aeschrightaeschright left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'd like you to redo the test using our standard test script: see scripts/maketest -- you can give it a directory fixture and it will give you the test setup. It'll help us keep things consistent and avoid some older conventions that aren't effective.

@larsgw

larsgw commented Jan 16, 2019

Copy link
Copy Markdown
ContributorAuthor

I added the tests, and a doc change now too. I think this change is going to be pretty controversial, seeing how many people already disagree with npm install behavior, fixing incorrect package-lock.json files. The only middle way I can think of, fixing the bug and keeping the behavior, would be to somehow merge all movements in the node_modules tree with all the modules left behind in the shrinkwrap tree, but I don't know if that's feasible.

@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 6ca2f43 to 6d0cc95CompareJanuary 18, 2019 19:17
@zkat
zkatforce-pushed the release-next branch 3 times, most recently from db63b89 to b09bc8cCompareJanuary 23, 2019 18:36
@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 06cdf5b to f957798CompareFebruary 20, 2019 20:42
@G-Rath

G-Rath commented May 14, 2019

Copy link
Copy Markdown
Contributor

What's the status of this? The community issue was closed, so I can't ask on that.

For me as a Windows user, with my expectation of what ddp does, I find it odd that running ddp results in a package-lock.json that will change if I then run npm i.

The core question I have is: Why is npm i able to generate a package-lock.json that includes fsevents without actually installing that package?

@FranklinYu

Copy link
Copy Markdown

@G-Rath I don’t think that’s an issue. Optional dependency should also be locked, so that when the package is installed on another platform, it won’t change the lock file; this way the lock file converges. In contrary, it doesn’t make sense to remove fsevents when it doesn’t apply to current platform.

@G-Rath

Copy link
Copy Markdown
Contributor

@FranklinYu For sure.

My question wasn't a challenge at npm i, but as a starting point to try and figure out how ddp should act, as this PR has the breaking side effect of :

installing any package that is in package-lock.json but not in node_modules

The point I was wanting to draw attention to with my question is that npm idoesn't have to install all packages to have them in the package-lock.json, and so why does ddp have to?

While I'm sure there's a good reason, in my eyes that highlights a starting point for identifying where ddp drifts away from i.

@FranklinYu

Copy link
Copy Markdown

installing any package that is in package-lock.json but not in node_modules

I think that only refers to packages that work for current platform. For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

@G-Rath

G-Rath commented Jun 10, 2019

Copy link
Copy Markdown
Contributor

For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Currently, if I run npm i on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get the same lock file regardless of OS.

Currently, if I run npm ddp on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get a lock file that will differ based on what packages are supported by that OS.

Example:

Given this package.json:

{
"optionalDependencies": {
"fsevents": "^2.0.7"
}
}

Running npm i on Windows, Linux, & OSX gives you the following lock:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Running npm ddp on Windows or Linux gives you:

{
"lockfileVersion": 1
}

Running npm ddp on Mac gives you:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Note that nothing in node_modules has changed - the only change happening here is in the package-lock.json (aside from npm i on OSX, which of course installs fsevents)

@FranklinYu

Copy link
Copy Markdown

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Exactly. The whole point of this PR is that dedupeshould not remove fsevents.

@G-Rath

Copy link
Copy Markdown
Contributor

The whole point of this PR is that dedupe should not remove fsevents.

Exactly. I've never said otherwise; but it's good we're on the same page 🙂

My question is about the side effect:

installing any package that is in package-lock.json but not in node_modules

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From my understanding, the worst-case bare minimal solution would be to simply run npm i after npm ddp. That is of course highly inefficient, but it's a starting point - from there you can look to strip away unneeded parts from npm i until you're left w/ the minimal code required to "fix" the side effect.

That's a "fix it in post" style approach - which isn't bad; those kind of fixes are often just far more simpler & maintainable than the alternative: try to "fix" the actual root cause of the problem.

This could just be b/c I'm misunderstanding what is meant by "installing any package that is in package-lock.json but not in node_modules", or that just due to the complex nature of npm, that's just how it is, but as I've said: there is at least one way to work around it, that I don't see any major downsides to, given that it seems to give what we all agree is the least astonishing result from running such a command.

Eitherway, I'm just generally interested in understanding more about npm works that means that this is a side effect (w/o spending hours taking it apart line by line*) 😄

*: I mean, I'd love to do this, but sadly don't have the time 😂

@larsgw

Copy link
Copy Markdown
ContributorAuthor

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From what I remember (it's been a while), npm dedupe works like this:

  1. Read node_modules/ tree (like npm ls, and not package-lock.json like npm i)
  2. Perform deduping on the tree
  3. Write the tree to node_modules/ and package-lock.json

Since non-installed optional dependencies have no record in the node_modules/ tree they aren't included in the new package-lock.json. To fix that, I see two possibilities:

  1. Read from package-lock.json instead of node_modules. This has the added side-effect of installing (or trying to install) any packages that are in the package lock, but not in node_modules. This is because only reading the package lock can't tell you certain packages shouldn't be installed, just like only reading node_modules can't tell you if certain packages should be installed. However, that seemed like a smaller problem to me.
  2. What I mentioned above: somehow track the movements of the deduping process and merge those with the package lock, but leave the rest of the process. Way above my pay-grade as a voluntary contributor (and please don't spend time doing that yourself either).

PS: I think I thought of a third option just now, but I can't think of it anymore so I'll come back to this if the thought does.

@G-Rath

Copy link
Copy Markdown
Contributor

Ah interesting - thanks for the explanation :)

Would it be viable to try and leverage the logic that npm i uses to determine if a package should be installed or not?

That could be going in the direction you said not spend time trying to do via 2. :)

I'm also wondering if there is any to know (or get an idea of) all the possible conditions that can result in a package not being installed but being written to package-lock.json.

So far I only know of optionalDependencies, which from what happens w/ fsevents, seem to always be included in the package-lock.json, and so wouldn't be something ddp has to worry about?

I feel like this strengthens the argument that ddp should optimise based on package-lock.json: while it's annoying, if you're going to install a dependency in some situations, you should optimise for that situation, since otherwise isn't it harder/more work to safely determine what packages should be installed?

This has the added side-effect of installing (or trying to install)

To me that sounds like it'll work, depending on how the "trying" is handled; Looking at the error output from doing npm i fsevents on Windows, it's throwing EBADPLATFORM - That to me seems like an exception that'd only really be thrown in a predictable situation (unlike say EBADPERMISSIONS^).

Which I think boils back down to the first part of this comment, about how does npm i determine if a package should be installed, and can that be leveraged here?

*: Luckly, fsevents has no dependencies, and from what I've seen, it's the most common optionalDependencies out there.
^: Might not be an actual exception, but you know what I mean

@FranklinYu

Copy link
Copy Markdown

I like option 1. Package lock file should be the source of truth. Everyone knows that node_modules is transient and nobody checks it into version control.

@G-Rath

G-Rath commented Jun 11, 2019

Copy link
Copy Markdown
Contributor

Plus if you do "need" to modify anything in node_modules, you can use patch-package to do so stably.

@FranklinYu

Copy link
Copy Markdown

sane seems to have switched from fsevents package to fs.watch(). For future testers, we have other examples:

Both depend on fsevents for now.

@jpsfs

jpsfs commented May 6, 2020

Copy link
Copy Markdown

@FranklinYu any update on this?

@FranklinYu

Copy link
Copy Markdown

@jpsfs I think you’re mentioning the wrong user. I’m not part of npm team so I can’t merge this even though I want this feature. I haven’t see any npm member involved in discussion by far, so I wouldn’t expect this to happen any time soon.

@darcyclarke

Copy link
Copy Markdown
Contributor

@larsgw sorry for the delay here. We're going to close this as much of this code has been moved to Arborist (dedupe has been rewritten in v7)

@FranklinYu

Copy link
Copy Markdown

@darcyclarke Did you imply that NPM v7 won’t have this issue any more?

@TimDaubTimDaub mentioned this pull request Mar 15, 2022
2 tasks
antongolub pushed a commit to antongolub-forks/npm-cli that referenced this pull request May 18, 2024
🤖 I have created a release *beep* *boop*
---
## [4.0.4](npm/bin-links@v4.0.3...v4.0.4)
(2024-05-04)
### Bug Fixes
*
[`100a4b7`](npm/bin-links@100a4b7)
[npm#117](npm/bin-links#117) linting:
no-unused-vars (@lukekarrys)
### Chores
*
[`e955437`](npm/bin-links@e955437)
[npm#117](npm/bin-links#117) bump
@npmcli/template-oss to 4.22.0 (@lukekarrys)
*
[`b602aca`](npm/bin-links@b602aca)
[npm#117](npm/bin-links#117) postinstall for
dependabot template-oss PR (@lukekarrys)
*
[`955cc34`](npm/bin-links@955cc34)
[npm#116](npm/bin-links#116) bump
@npmcli/template-oss from 4.21.3 to 4.21.4 (@dependabot[bot])
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 2, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 13, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:majorbackwards-incompatible breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@larsgw@aeschright@G-Rath@FranklinYu@jpsfs@darcyclarke@zkat
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' dedupe: get deps from shrinkwrap by larsgw · Pull Request #118 · npm/cli · GitHub
Skip to content

dedupe: get deps from shrinkwrap - #118

Closed
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12
Closed

dedupe: get deps from shrinkwrap#118
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12

Conversation

@larsgw

Copy link
Copy Markdown
Contributor

@larsgw
larsgw requested a review from a team as a code ownerDecember 13, 2018 22:35
@larsgw

Copy link
Copy Markdown
ContributorAuthor

(Breaking) side effects include:

  • installing any package that is in package-lock.json but not in node_modules

It doesn't install packages that are only specified in package.json.

@zkatzkat added the semver:major backwards-incompatible breaking changes label Jan 7, 2019
@aeschright

Copy link
Copy Markdown
Contributor

We're going to make sure this doesn't have any unintended consequences and goes in the right direction.

@aeschrightaeschright left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'd like you to redo the test using our standard test script: see scripts/maketest -- you can give it a directory fixture and it will give you the test setup. It'll help us keep things consistent and avoid some older conventions that aren't effective.

@larsgw

larsgw commented Jan 16, 2019

Copy link
Copy Markdown
ContributorAuthor

I added the tests, and a doc change now too. I think this change is going to be pretty controversial, seeing how many people already disagree with npm install behavior, fixing incorrect package-lock.json files. The only middle way I can think of, fixing the bug and keeping the behavior, would be to somehow merge all movements in the node_modules tree with all the modules left behind in the shrinkwrap tree, but I don't know if that's feasible.

@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 6ca2f43 to 6d0cc95CompareJanuary 18, 2019 19:17
@zkat
zkatforce-pushed the release-next branch 3 times, most recently from db63b89 to b09bc8cCompareJanuary 23, 2019 18:36
@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 06cdf5b to f957798CompareFebruary 20, 2019 20:42
@G-Rath

G-Rath commented May 14, 2019

Copy link
Copy Markdown
Contributor

What's the status of this? The community issue was closed, so I can't ask on that.

For me as a Windows user, with my expectation of what ddp does, I find it odd that running ddp results in a package-lock.json that will change if I then run npm i.

The core question I have is: Why is npm i able to generate a package-lock.json that includes fsevents without actually installing that package?

@FranklinYu

Copy link
Copy Markdown

@G-Rath I don’t think that’s an issue. Optional dependency should also be locked, so that when the package is installed on another platform, it won’t change the lock file; this way the lock file converges. In contrary, it doesn’t make sense to remove fsevents when it doesn’t apply to current platform.

@G-Rath

Copy link
Copy Markdown
Contributor

@FranklinYu For sure.

My question wasn't a challenge at npm i, but as a starting point to try and figure out how ddp should act, as this PR has the breaking side effect of :

installing any package that is in package-lock.json but not in node_modules

The point I was wanting to draw attention to with my question is that npm idoesn't have to install all packages to have them in the package-lock.json, and so why does ddp have to?

While I'm sure there's a good reason, in my eyes that highlights a starting point for identifying where ddp drifts away from i.

@FranklinYu

Copy link
Copy Markdown

installing any package that is in package-lock.json but not in node_modules

I think that only refers to packages that work for current platform. For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

@G-Rath

G-Rath commented Jun 10, 2019

Copy link
Copy Markdown
Contributor

For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Currently, if I run npm i on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get the same lock file regardless of OS.

Currently, if I run npm ddp on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get a lock file that will differ based on what packages are supported by that OS.

Example:

Given this package.json:

{
"optionalDependencies": {
"fsevents": "^2.0.7"
}
}

Running npm i on Windows, Linux, & OSX gives you the following lock:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Running npm ddp on Windows or Linux gives you:

{
"lockfileVersion": 1
}

Running npm ddp on Mac gives you:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Note that nothing in node_modules has changed - the only change happening here is in the package-lock.json (aside from npm i on OSX, which of course installs fsevents)

@FranklinYu

Copy link
Copy Markdown

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Exactly. The whole point of this PR is that dedupeshould not remove fsevents.

@G-Rath

Copy link
Copy Markdown
Contributor

The whole point of this PR is that dedupe should not remove fsevents.

Exactly. I've never said otherwise; but it's good we're on the same page 🙂

My question is about the side effect:

installing any package that is in package-lock.json but not in node_modules

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From my understanding, the worst-case bare minimal solution would be to simply run npm i after npm ddp. That is of course highly inefficient, but it's a starting point - from there you can look to strip away unneeded parts from npm i until you're left w/ the minimal code required to "fix" the side effect.

That's a "fix it in post" style approach - which isn't bad; those kind of fixes are often just far more simpler & maintainable than the alternative: try to "fix" the actual root cause of the problem.

This could just be b/c I'm misunderstanding what is meant by "installing any package that is in package-lock.json but not in node_modules", or that just due to the complex nature of npm, that's just how it is, but as I've said: there is at least one way to work around it, that I don't see any major downsides to, given that it seems to give what we all agree is the least astonishing result from running such a command.

Eitherway, I'm just generally interested in understanding more about npm works that means that this is a side effect (w/o spending hours taking it apart line by line*) 😄

*: I mean, I'd love to do this, but sadly don't have the time 😂

@larsgw

Copy link
Copy Markdown
ContributorAuthor

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From what I remember (it's been a while), npm dedupe works like this:

  1. Read node_modules/ tree (like npm ls, and not package-lock.json like npm i)
  2. Perform deduping on the tree
  3. Write the tree to node_modules/ and package-lock.json

Since non-installed optional dependencies have no record in the node_modules/ tree they aren't included in the new package-lock.json. To fix that, I see two possibilities:

  1. Read from package-lock.json instead of node_modules. This has the added side-effect of installing (or trying to install) any packages that are in the package lock, but not in node_modules. This is because only reading the package lock can't tell you certain packages shouldn't be installed, just like only reading node_modules can't tell you if certain packages should be installed. However, that seemed like a smaller problem to me.
  2. What I mentioned above: somehow track the movements of the deduping process and merge those with the package lock, but leave the rest of the process. Way above my pay-grade as a voluntary contributor (and please don't spend time doing that yourself either).

PS: I think I thought of a third option just now, but I can't think of it anymore so I'll come back to this if the thought does.

@G-Rath

Copy link
Copy Markdown
Contributor

Ah interesting - thanks for the explanation :)

Would it be viable to try and leverage the logic that npm i uses to determine if a package should be installed or not?

That could be going in the direction you said not spend time trying to do via 2. :)

I'm also wondering if there is any to know (or get an idea of) all the possible conditions that can result in a package not being installed but being written to package-lock.json.

So far I only know of optionalDependencies, which from what happens w/ fsevents, seem to always be included in the package-lock.json, and so wouldn't be something ddp has to worry about?

I feel like this strengthens the argument that ddp should optimise based on package-lock.json: while it's annoying, if you're going to install a dependency in some situations, you should optimise for that situation, since otherwise isn't it harder/more work to safely determine what packages should be installed?

This has the added side-effect of installing (or trying to install)

To me that sounds like it'll work, depending on how the "trying" is handled; Looking at the error output from doing npm i fsevents on Windows, it's throwing EBADPLATFORM - That to me seems like an exception that'd only really be thrown in a predictable situation (unlike say EBADPERMISSIONS^).

Which I think boils back down to the first part of this comment, about how does npm i determine if a package should be installed, and can that be leveraged here?

*: Luckly, fsevents has no dependencies, and from what I've seen, it's the most common optionalDependencies out there.
^: Might not be an actual exception, but you know what I mean

@FranklinYu

Copy link
Copy Markdown

I like option 1. Package lock file should be the source of truth. Everyone knows that node_modules is transient and nobody checks it into version control.

@G-Rath

G-Rath commented Jun 11, 2019

Copy link
Copy Markdown
Contributor

Plus if you do "need" to modify anything in node_modules, you can use patch-package to do so stably.

@FranklinYu

Copy link
Copy Markdown

sane seems to have switched from fsevents package to fs.watch(). For future testers, we have other examples:

Both depend on fsevents for now.

@jpsfs

jpsfs commented May 6, 2020

Copy link
Copy Markdown

@FranklinYu any update on this?

@FranklinYu

Copy link
Copy Markdown

@jpsfs I think you’re mentioning the wrong user. I’m not part of npm team so I can’t merge this even though I want this feature. I haven’t see any npm member involved in discussion by far, so I wouldn’t expect this to happen any time soon.

@darcyclarke

Copy link
Copy Markdown
Contributor

@larsgw sorry for the delay here. We're going to close this as much of this code has been moved to Arborist (dedupe has been rewritten in v7)

@FranklinYu

Copy link
Copy Markdown

@darcyclarke Did you imply that NPM v7 won’t have this issue any more?

@TimDaubTimDaub mentioned this pull request Mar 15, 2022
2 tasks
antongolub pushed a commit to antongolub-forks/npm-cli that referenced this pull request May 18, 2024
🤖 I have created a release *beep* *boop*
---
## [4.0.4](npm/bin-links@v4.0.3...v4.0.4)
(2024-05-04)
### Bug Fixes
*
[`100a4b7`](npm/bin-links@100a4b7)
[npm#117](npm/bin-links#117) linting:
no-unused-vars (@lukekarrys)
### Chores
*
[`e955437`](npm/bin-links@e955437)
[npm#117](npm/bin-links#117) bump
@npmcli/template-oss to 4.22.0 (@lukekarrys)
*
[`b602aca`](npm/bin-links@b602aca)
[npm#117](npm/bin-links#117) postinstall for
dependabot template-oss PR (@lukekarrys)
*
[`955cc34`](npm/bin-links@955cc34)
[npm#116](npm/bin-links#116) bump
@npmcli/template-oss from 4.21.3 to 4.21.4 (@dependabot[bot])
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 2, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 13, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:majorbackwards-incompatible breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@larsgw@aeschright@G-Rath@FranklinYu@jpsfs@darcyclarke@zkat
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' dedupe: get deps from shrinkwrap by larsgw · Pull Request #118 · npm/cli · GitHub
Skip to content

dedupe: get deps from shrinkwrap - #118

Closed
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12
Closed

dedupe: get deps from shrinkwrap#118
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12

Conversation

@larsgw

Copy link
Copy Markdown
Contributor

@larsgw
larsgw requested a review from a team as a code ownerDecember 13, 2018 22:35
@larsgw

Copy link
Copy Markdown
ContributorAuthor

(Breaking) side effects include:

  • installing any package that is in package-lock.json but not in node_modules

It doesn't install packages that are only specified in package.json.

@zkatzkat added the semver:major backwards-incompatible breaking changes label Jan 7, 2019
@aeschright

Copy link
Copy Markdown
Contributor

We're going to make sure this doesn't have any unintended consequences and goes in the right direction.

@aeschrightaeschright left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'd like you to redo the test using our standard test script: see scripts/maketest -- you can give it a directory fixture and it will give you the test setup. It'll help us keep things consistent and avoid some older conventions that aren't effective.

@larsgw

larsgw commented Jan 16, 2019

Copy link
Copy Markdown
ContributorAuthor

I added the tests, and a doc change now too. I think this change is going to be pretty controversial, seeing how many people already disagree with npm install behavior, fixing incorrect package-lock.json files. The only middle way I can think of, fixing the bug and keeping the behavior, would be to somehow merge all movements in the node_modules tree with all the modules left behind in the shrinkwrap tree, but I don't know if that's feasible.

@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 6ca2f43 to 6d0cc95CompareJanuary 18, 2019 19:17
@zkat
zkatforce-pushed the release-next branch 3 times, most recently from db63b89 to b09bc8cCompareJanuary 23, 2019 18:36
@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 06cdf5b to f957798CompareFebruary 20, 2019 20:42
@G-Rath

G-Rath commented May 14, 2019

Copy link
Copy Markdown
Contributor

What's the status of this? The community issue was closed, so I can't ask on that.

For me as a Windows user, with my expectation of what ddp does, I find it odd that running ddp results in a package-lock.json that will change if I then run npm i.

The core question I have is: Why is npm i able to generate a package-lock.json that includes fsevents without actually installing that package?

@FranklinYu

Copy link
Copy Markdown

@G-Rath I don’t think that’s an issue. Optional dependency should also be locked, so that when the package is installed on another platform, it won’t change the lock file; this way the lock file converges. In contrary, it doesn’t make sense to remove fsevents when it doesn’t apply to current platform.

@G-Rath

Copy link
Copy Markdown
Contributor

@FranklinYu For sure.

My question wasn't a challenge at npm i, but as a starting point to try and figure out how ddp should act, as this PR has the breaking side effect of :

installing any package that is in package-lock.json but not in node_modules

The point I was wanting to draw attention to with my question is that npm idoesn't have to install all packages to have them in the package-lock.json, and so why does ddp have to?

While I'm sure there's a good reason, in my eyes that highlights a starting point for identifying where ddp drifts away from i.

@FranklinYu

Copy link
Copy Markdown

installing any package that is in package-lock.json but not in node_modules

I think that only refers to packages that work for current platform. For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

@G-Rath

G-Rath commented Jun 10, 2019

Copy link
Copy Markdown
Contributor

For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Currently, if I run npm i on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get the same lock file regardless of OS.

Currently, if I run npm ddp on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get a lock file that will differ based on what packages are supported by that OS.

Example:

Given this package.json:

{
"optionalDependencies": {
"fsevents": "^2.0.7"
}
}

Running npm i on Windows, Linux, & OSX gives you the following lock:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Running npm ddp on Windows or Linux gives you:

{
"lockfileVersion": 1
}

Running npm ddp on Mac gives you:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Note that nothing in node_modules has changed - the only change happening here is in the package-lock.json (aside from npm i on OSX, which of course installs fsevents)

@FranklinYu

Copy link
Copy Markdown

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Exactly. The whole point of this PR is that dedupeshould not remove fsevents.

@G-Rath

Copy link
Copy Markdown
Contributor

The whole point of this PR is that dedupe should not remove fsevents.

Exactly. I've never said otherwise; but it's good we're on the same page 🙂

My question is about the side effect:

installing any package that is in package-lock.json but not in node_modules

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From my understanding, the worst-case bare minimal solution would be to simply run npm i after npm ddp. That is of course highly inefficient, but it's a starting point - from there you can look to strip away unneeded parts from npm i until you're left w/ the minimal code required to "fix" the side effect.

That's a "fix it in post" style approach - which isn't bad; those kind of fixes are often just far more simpler & maintainable than the alternative: try to "fix" the actual root cause of the problem.

This could just be b/c I'm misunderstanding what is meant by "installing any package that is in package-lock.json but not in node_modules", or that just due to the complex nature of npm, that's just how it is, but as I've said: there is at least one way to work around it, that I don't see any major downsides to, given that it seems to give what we all agree is the least astonishing result from running such a command.

Eitherway, I'm just generally interested in understanding more about npm works that means that this is a side effect (w/o spending hours taking it apart line by line*) 😄

*: I mean, I'd love to do this, but sadly don't have the time 😂

@larsgw

Copy link
Copy Markdown
ContributorAuthor

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From what I remember (it's been a while), npm dedupe works like this:

  1. Read node_modules/ tree (like npm ls, and not package-lock.json like npm i)
  2. Perform deduping on the tree
  3. Write the tree to node_modules/ and package-lock.json

Since non-installed optional dependencies have no record in the node_modules/ tree they aren't included in the new package-lock.json. To fix that, I see two possibilities:

  1. Read from package-lock.json instead of node_modules. This has the added side-effect of installing (or trying to install) any packages that are in the package lock, but not in node_modules. This is because only reading the package lock can't tell you certain packages shouldn't be installed, just like only reading node_modules can't tell you if certain packages should be installed. However, that seemed like a smaller problem to me.
  2. What I mentioned above: somehow track the movements of the deduping process and merge those with the package lock, but leave the rest of the process. Way above my pay-grade as a voluntary contributor (and please don't spend time doing that yourself either).

PS: I think I thought of a third option just now, but I can't think of it anymore so I'll come back to this if the thought does.

@G-Rath

Copy link
Copy Markdown
Contributor

Ah interesting - thanks for the explanation :)

Would it be viable to try and leverage the logic that npm i uses to determine if a package should be installed or not?

That could be going in the direction you said not spend time trying to do via 2. :)

I'm also wondering if there is any to know (or get an idea of) all the possible conditions that can result in a package not being installed but being written to package-lock.json.

So far I only know of optionalDependencies, which from what happens w/ fsevents, seem to always be included in the package-lock.json, and so wouldn't be something ddp has to worry about?

I feel like this strengthens the argument that ddp should optimise based on package-lock.json: while it's annoying, if you're going to install a dependency in some situations, you should optimise for that situation, since otherwise isn't it harder/more work to safely determine what packages should be installed?

This has the added side-effect of installing (or trying to install)

To me that sounds like it'll work, depending on how the "trying" is handled; Looking at the error output from doing npm i fsevents on Windows, it's throwing EBADPLATFORM - That to me seems like an exception that'd only really be thrown in a predictable situation (unlike say EBADPERMISSIONS^).

Which I think boils back down to the first part of this comment, about how does npm i determine if a package should be installed, and can that be leveraged here?

*: Luckly, fsevents has no dependencies, and from what I've seen, it's the most common optionalDependencies out there.
^: Might not be an actual exception, but you know what I mean

@FranklinYu

Copy link
Copy Markdown

I like option 1. Package lock file should be the source of truth. Everyone knows that node_modules is transient and nobody checks it into version control.

@G-Rath

G-Rath commented Jun 11, 2019

Copy link
Copy Markdown
Contributor

Plus if you do "need" to modify anything in node_modules, you can use patch-package to do so stably.

@FranklinYu

Copy link
Copy Markdown

sane seems to have switched from fsevents package to fs.watch(). For future testers, we have other examples:

Both depend on fsevents for now.

@jpsfs

jpsfs commented May 6, 2020

Copy link
Copy Markdown

@FranklinYu any update on this?

@FranklinYu

Copy link
Copy Markdown

@jpsfs I think you’re mentioning the wrong user. I’m not part of npm team so I can’t merge this even though I want this feature. I haven’t see any npm member involved in discussion by far, so I wouldn’t expect this to happen any time soon.

@darcyclarke

Copy link
Copy Markdown
Contributor

@larsgw sorry for the delay here. We're going to close this as much of this code has been moved to Arborist (dedupe has been rewritten in v7)

@FranklinYu

Copy link
Copy Markdown

@darcyclarke Did you imply that NPM v7 won’t have this issue any more?

@TimDaubTimDaub mentioned this pull request Mar 15, 2022
2 tasks
antongolub pushed a commit to antongolub-forks/npm-cli that referenced this pull request May 18, 2024
🤖 I have created a release *beep* *boop*
---
## [4.0.4](npm/bin-links@v4.0.3...v4.0.4)
(2024-05-04)
### Bug Fixes
*
[`100a4b7`](npm/bin-links@100a4b7)
[npm#117](npm/bin-links#117) linting:
no-unused-vars (@lukekarrys)
### Chores
*
[`e955437`](npm/bin-links@e955437)
[npm#117](npm/bin-links#117) bump
@npmcli/template-oss to 4.22.0 (@lukekarrys)
*
[`b602aca`](npm/bin-links@b602aca)
[npm#117](npm/bin-links#117) postinstall for
dependabot template-oss PR (@lukekarrys)
*
[`955cc34`](npm/bin-links@955cc34)
[npm#116](npm/bin-links#116) bump
@npmcli/template-oss from 4.21.3 to 4.21.4 (@dependabot[bot])
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 2, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 13, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:majorbackwards-incompatible breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@larsgw@aeschright@G-Rath@FranklinYu@jpsfs@darcyclarke@zkat
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' dedupe: get deps from shrinkwrap by larsgw · Pull Request #118 · npm/cli · GitHub
Skip to content

dedupe: get deps from shrinkwrap - #118

Closed
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12
Closed

dedupe: get deps from shrinkwrap#118
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12

Conversation

@larsgw

Copy link
Copy Markdown
Contributor

@larsgw
larsgw requested a review from a team as a code ownerDecember 13, 2018 22:35
@larsgw

Copy link
Copy Markdown
ContributorAuthor

(Breaking) side effects include:

  • installing any package that is in package-lock.json but not in node_modules

It doesn't install packages that are only specified in package.json.

@zkatzkat added the semver:major backwards-incompatible breaking changes label Jan 7, 2019
@aeschright

Copy link
Copy Markdown
Contributor

We're going to make sure this doesn't have any unintended consequences and goes in the right direction.

@aeschrightaeschright left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'd like you to redo the test using our standard test script: see scripts/maketest -- you can give it a directory fixture and it will give you the test setup. It'll help us keep things consistent and avoid some older conventions that aren't effective.

@larsgw

larsgw commented Jan 16, 2019

Copy link
Copy Markdown
ContributorAuthor

I added the tests, and a doc change now too. I think this change is going to be pretty controversial, seeing how many people already disagree with npm install behavior, fixing incorrect package-lock.json files. The only middle way I can think of, fixing the bug and keeping the behavior, would be to somehow merge all movements in the node_modules tree with all the modules left behind in the shrinkwrap tree, but I don't know if that's feasible.

@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 6ca2f43 to 6d0cc95CompareJanuary 18, 2019 19:17
@zkat
zkatforce-pushed the release-next branch 3 times, most recently from db63b89 to b09bc8cCompareJanuary 23, 2019 18:36
@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 06cdf5b to f957798CompareFebruary 20, 2019 20:42
@G-Rath

G-Rath commented May 14, 2019

Copy link
Copy Markdown
Contributor

What's the status of this? The community issue was closed, so I can't ask on that.

For me as a Windows user, with my expectation of what ddp does, I find it odd that running ddp results in a package-lock.json that will change if I then run npm i.

The core question I have is: Why is npm i able to generate a package-lock.json that includes fsevents without actually installing that package?

@FranklinYu

Copy link
Copy Markdown

@G-Rath I don’t think that’s an issue. Optional dependency should also be locked, so that when the package is installed on another platform, it won’t change the lock file; this way the lock file converges. In contrary, it doesn’t make sense to remove fsevents when it doesn’t apply to current platform.

@G-Rath

Copy link
Copy Markdown
Contributor

@FranklinYu For sure.

My question wasn't a challenge at npm i, but as a starting point to try and figure out how ddp should act, as this PR has the breaking side effect of :

installing any package that is in package-lock.json but not in node_modules

The point I was wanting to draw attention to with my question is that npm idoesn't have to install all packages to have them in the package-lock.json, and so why does ddp have to?

While I'm sure there's a good reason, in my eyes that highlights a starting point for identifying where ddp drifts away from i.

@FranklinYu

Copy link
Copy Markdown

installing any package that is in package-lock.json but not in node_modules

I think that only refers to packages that work for current platform. For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

@G-Rath

G-Rath commented Jun 10, 2019

Copy link
Copy Markdown
Contributor

For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Currently, if I run npm i on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get the same lock file regardless of OS.

Currently, if I run npm ddp on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get a lock file that will differ based on what packages are supported by that OS.

Example:

Given this package.json:

{
"optionalDependencies": {
"fsevents": "^2.0.7"
}
}

Running npm i on Windows, Linux, & OSX gives you the following lock:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Running npm ddp on Windows or Linux gives you:

{
"lockfileVersion": 1
}

Running npm ddp on Mac gives you:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Note that nothing in node_modules has changed - the only change happening here is in the package-lock.json (aside from npm i on OSX, which of course installs fsevents)

@FranklinYu

Copy link
Copy Markdown

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Exactly. The whole point of this PR is that dedupeshould not remove fsevents.

@G-Rath

Copy link
Copy Markdown
Contributor

The whole point of this PR is that dedupe should not remove fsevents.

Exactly. I've never said otherwise; but it's good we're on the same page 🙂

My question is about the side effect:

installing any package that is in package-lock.json but not in node_modules

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From my understanding, the worst-case bare minimal solution would be to simply run npm i after npm ddp. That is of course highly inefficient, but it's a starting point - from there you can look to strip away unneeded parts from npm i until you're left w/ the minimal code required to "fix" the side effect.

That's a "fix it in post" style approach - which isn't bad; those kind of fixes are often just far more simpler & maintainable than the alternative: try to "fix" the actual root cause of the problem.

This could just be b/c I'm misunderstanding what is meant by "installing any package that is in package-lock.json but not in node_modules", or that just due to the complex nature of npm, that's just how it is, but as I've said: there is at least one way to work around it, that I don't see any major downsides to, given that it seems to give what we all agree is the least astonishing result from running such a command.

Eitherway, I'm just generally interested in understanding more about npm works that means that this is a side effect (w/o spending hours taking it apart line by line*) 😄

*: I mean, I'd love to do this, but sadly don't have the time 😂

@larsgw

Copy link
Copy Markdown
ContributorAuthor

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From what I remember (it's been a while), npm dedupe works like this:

  1. Read node_modules/ tree (like npm ls, and not package-lock.json like npm i)
  2. Perform deduping on the tree
  3. Write the tree to node_modules/ and package-lock.json

Since non-installed optional dependencies have no record in the node_modules/ tree they aren't included in the new package-lock.json. To fix that, I see two possibilities:

  1. Read from package-lock.json instead of node_modules. This has the added side-effect of installing (or trying to install) any packages that are in the package lock, but not in node_modules. This is because only reading the package lock can't tell you certain packages shouldn't be installed, just like only reading node_modules can't tell you if certain packages should be installed. However, that seemed like a smaller problem to me.
  2. What I mentioned above: somehow track the movements of the deduping process and merge those with the package lock, but leave the rest of the process. Way above my pay-grade as a voluntary contributor (and please don't spend time doing that yourself either).

PS: I think I thought of a third option just now, but I can't think of it anymore so I'll come back to this if the thought does.

@G-Rath

Copy link
Copy Markdown
Contributor

Ah interesting - thanks for the explanation :)

Would it be viable to try and leverage the logic that npm i uses to determine if a package should be installed or not?

That could be going in the direction you said not spend time trying to do via 2. :)

I'm also wondering if there is any to know (or get an idea of) all the possible conditions that can result in a package not being installed but being written to package-lock.json.

So far I only know of optionalDependencies, which from what happens w/ fsevents, seem to always be included in the package-lock.json, and so wouldn't be something ddp has to worry about?

I feel like this strengthens the argument that ddp should optimise based on package-lock.json: while it's annoying, if you're going to install a dependency in some situations, you should optimise for that situation, since otherwise isn't it harder/more work to safely determine what packages should be installed?

This has the added side-effect of installing (or trying to install)

To me that sounds like it'll work, depending on how the "trying" is handled; Looking at the error output from doing npm i fsevents on Windows, it's throwing EBADPLATFORM - That to me seems like an exception that'd only really be thrown in a predictable situation (unlike say EBADPERMISSIONS^).

Which I think boils back down to the first part of this comment, about how does npm i determine if a package should be installed, and can that be leveraged here?

*: Luckly, fsevents has no dependencies, and from what I've seen, it's the most common optionalDependencies out there.
^: Might not be an actual exception, but you know what I mean

@FranklinYu

Copy link
Copy Markdown

I like option 1. Package lock file should be the source of truth. Everyone knows that node_modules is transient and nobody checks it into version control.

@G-Rath

G-Rath commented Jun 11, 2019

Copy link
Copy Markdown
Contributor

Plus if you do "need" to modify anything in node_modules, you can use patch-package to do so stably.

@FranklinYu

Copy link
Copy Markdown

sane seems to have switched from fsevents package to fs.watch(). For future testers, we have other examples:

Both depend on fsevents for now.

@jpsfs

jpsfs commented May 6, 2020

Copy link
Copy Markdown

@FranklinYu any update on this?

@FranklinYu

Copy link
Copy Markdown

@jpsfs I think you’re mentioning the wrong user. I’m not part of npm team so I can’t merge this even though I want this feature. I haven’t see any npm member involved in discussion by far, so I wouldn’t expect this to happen any time soon.

@darcyclarke

Copy link
Copy Markdown
Contributor

@larsgw sorry for the delay here. We're going to close this as much of this code has been moved to Arborist (dedupe has been rewritten in v7)

@FranklinYu

Copy link
Copy Markdown

@darcyclarke Did you imply that NPM v7 won’t have this issue any more?

@TimDaubTimDaub mentioned this pull request Mar 15, 2022
2 tasks
antongolub pushed a commit to antongolub-forks/npm-cli that referenced this pull request May 18, 2024
🤖 I have created a release *beep* *boop*
---
## [4.0.4](npm/bin-links@v4.0.3...v4.0.4)
(2024-05-04)
### Bug Fixes
*
[`100a4b7`](npm/bin-links@100a4b7)
[npm#117](npm/bin-links#117) linting:
no-unused-vars (@lukekarrys)
### Chores
*
[`e955437`](npm/bin-links@e955437)
[npm#117](npm/bin-links#117) bump
@npmcli/template-oss to 4.22.0 (@lukekarrys)
*
[`b602aca`](npm/bin-links@b602aca)
[npm#117](npm/bin-links#117) postinstall for
dependabot template-oss PR (@lukekarrys)
*
[`955cc34`](npm/bin-links@955cc34)
[npm#116](npm/bin-links#116) bump
@npmcli/template-oss from 4.21.3 to 4.21.4 (@dependabot[bot])
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 2, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 13, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:majorbackwards-incompatible breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@larsgw@aeschright@G-Rath@FranklinYu@jpsfs@darcyclarke@zkat
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' dedupe: get deps from shrinkwrap by larsgw · Pull Request #118 · npm/cli · GitHub
Skip to content

dedupe: get deps from shrinkwrap - #118

Closed
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12
Closed

dedupe: get deps from shrinkwrap#118
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12

Conversation

@larsgw

Copy link
Copy Markdown
Contributor

@larsgw
larsgw requested a review from a team as a code ownerDecember 13, 2018 22:35
@larsgw

Copy link
Copy Markdown
ContributorAuthor

(Breaking) side effects include:

  • installing any package that is in package-lock.json but not in node_modules

It doesn't install packages that are only specified in package.json.

@zkatzkat added the semver:major backwards-incompatible breaking changes label Jan 7, 2019
@aeschright

Copy link
Copy Markdown
Contributor

We're going to make sure this doesn't have any unintended consequences and goes in the right direction.

@aeschrightaeschright left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'd like you to redo the test using our standard test script: see scripts/maketest -- you can give it a directory fixture and it will give you the test setup. It'll help us keep things consistent and avoid some older conventions that aren't effective.

@larsgw

larsgw commented Jan 16, 2019

Copy link
Copy Markdown
ContributorAuthor

I added the tests, and a doc change now too. I think this change is going to be pretty controversial, seeing how many people already disagree with npm install behavior, fixing incorrect package-lock.json files. The only middle way I can think of, fixing the bug and keeping the behavior, would be to somehow merge all movements in the node_modules tree with all the modules left behind in the shrinkwrap tree, but I don't know if that's feasible.

@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 6ca2f43 to 6d0cc95CompareJanuary 18, 2019 19:17
@zkat
zkatforce-pushed the release-next branch 3 times, most recently from db63b89 to b09bc8cCompareJanuary 23, 2019 18:36
@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 06cdf5b to f957798CompareFebruary 20, 2019 20:42
@G-Rath

G-Rath commented May 14, 2019

Copy link
Copy Markdown
Contributor

What's the status of this? The community issue was closed, so I can't ask on that.

For me as a Windows user, with my expectation of what ddp does, I find it odd that running ddp results in a package-lock.json that will change if I then run npm i.

The core question I have is: Why is npm i able to generate a package-lock.json that includes fsevents without actually installing that package?

@FranklinYu

Copy link
Copy Markdown

@G-Rath I don’t think that’s an issue. Optional dependency should also be locked, so that when the package is installed on another platform, it won’t change the lock file; this way the lock file converges. In contrary, it doesn’t make sense to remove fsevents when it doesn’t apply to current platform.

@G-Rath

Copy link
Copy Markdown
Contributor

@FranklinYu For sure.

My question wasn't a challenge at npm i, but as a starting point to try and figure out how ddp should act, as this PR has the breaking side effect of :

installing any package that is in package-lock.json but not in node_modules

The point I was wanting to draw attention to with my question is that npm idoesn't have to install all packages to have them in the package-lock.json, and so why does ddp have to?

While I'm sure there's a good reason, in my eyes that highlights a starting point for identifying where ddp drifts away from i.

@FranklinYu

Copy link
Copy Markdown

installing any package that is in package-lock.json but not in node_modules

I think that only refers to packages that work for current platform. For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

@G-Rath

G-Rath commented Jun 10, 2019

Copy link
Copy Markdown
Contributor

For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Currently, if I run npm i on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get the same lock file regardless of OS.

Currently, if I run npm ddp on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get a lock file that will differ based on what packages are supported by that OS.

Example:

Given this package.json:

{
"optionalDependencies": {
"fsevents": "^2.0.7"
}
}

Running npm i on Windows, Linux, & OSX gives you the following lock:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Running npm ddp on Windows or Linux gives you:

{
"lockfileVersion": 1
}

Running npm ddp on Mac gives you:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Note that nothing in node_modules has changed - the only change happening here is in the package-lock.json (aside from npm i on OSX, which of course installs fsevents)

@FranklinYu

Copy link
Copy Markdown

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Exactly. The whole point of this PR is that dedupeshould not remove fsevents.

@G-Rath

Copy link
Copy Markdown
Contributor

The whole point of this PR is that dedupe should not remove fsevents.

Exactly. I've never said otherwise; but it's good we're on the same page 🙂

My question is about the side effect:

installing any package that is in package-lock.json but not in node_modules

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From my understanding, the worst-case bare minimal solution would be to simply run npm i after npm ddp. That is of course highly inefficient, but it's a starting point - from there you can look to strip away unneeded parts from npm i until you're left w/ the minimal code required to "fix" the side effect.

That's a "fix it in post" style approach - which isn't bad; those kind of fixes are often just far more simpler & maintainable than the alternative: try to "fix" the actual root cause of the problem.

This could just be b/c I'm misunderstanding what is meant by "installing any package that is in package-lock.json but not in node_modules", or that just due to the complex nature of npm, that's just how it is, but as I've said: there is at least one way to work around it, that I don't see any major downsides to, given that it seems to give what we all agree is the least astonishing result from running such a command.

Eitherway, I'm just generally interested in understanding more about npm works that means that this is a side effect (w/o spending hours taking it apart line by line*) 😄

*: I mean, I'd love to do this, but sadly don't have the time 😂

@larsgw

Copy link
Copy Markdown
ContributorAuthor

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From what I remember (it's been a while), npm dedupe works like this:

  1. Read node_modules/ tree (like npm ls, and not package-lock.json like npm i)
  2. Perform deduping on the tree
  3. Write the tree to node_modules/ and package-lock.json

Since non-installed optional dependencies have no record in the node_modules/ tree they aren't included in the new package-lock.json. To fix that, I see two possibilities:

  1. Read from package-lock.json instead of node_modules. This has the added side-effect of installing (or trying to install) any packages that are in the package lock, but not in node_modules. This is because only reading the package lock can't tell you certain packages shouldn't be installed, just like only reading node_modules can't tell you if certain packages should be installed. However, that seemed like a smaller problem to me.
  2. What I mentioned above: somehow track the movements of the deduping process and merge those with the package lock, but leave the rest of the process. Way above my pay-grade as a voluntary contributor (and please don't spend time doing that yourself either).

PS: I think I thought of a third option just now, but I can't think of it anymore so I'll come back to this if the thought does.

@G-Rath

Copy link
Copy Markdown
Contributor

Ah interesting - thanks for the explanation :)

Would it be viable to try and leverage the logic that npm i uses to determine if a package should be installed or not?

That could be going in the direction you said not spend time trying to do via 2. :)

I'm also wondering if there is any to know (or get an idea of) all the possible conditions that can result in a package not being installed but being written to package-lock.json.

So far I only know of optionalDependencies, which from what happens w/ fsevents, seem to always be included in the package-lock.json, and so wouldn't be something ddp has to worry about?

I feel like this strengthens the argument that ddp should optimise based on package-lock.json: while it's annoying, if you're going to install a dependency in some situations, you should optimise for that situation, since otherwise isn't it harder/more work to safely determine what packages should be installed?

This has the added side-effect of installing (or trying to install)

To me that sounds like it'll work, depending on how the "trying" is handled; Looking at the error output from doing npm i fsevents on Windows, it's throwing EBADPLATFORM - That to me seems like an exception that'd only really be thrown in a predictable situation (unlike say EBADPERMISSIONS^).

Which I think boils back down to the first part of this comment, about how does npm i determine if a package should be installed, and can that be leveraged here?

*: Luckly, fsevents has no dependencies, and from what I've seen, it's the most common optionalDependencies out there.
^: Might not be an actual exception, but you know what I mean

@FranklinYu

Copy link
Copy Markdown

I like option 1. Package lock file should be the source of truth. Everyone knows that node_modules is transient and nobody checks it into version control.

@G-Rath

G-Rath commented Jun 11, 2019

Copy link
Copy Markdown
Contributor

Plus if you do "need" to modify anything in node_modules, you can use patch-package to do so stably.

@FranklinYu

Copy link
Copy Markdown

sane seems to have switched from fsevents package to fs.watch(). For future testers, we have other examples:

Both depend on fsevents for now.

@jpsfs

jpsfs commented May 6, 2020

Copy link
Copy Markdown

@FranklinYu any update on this?

@FranklinYu

Copy link
Copy Markdown

@jpsfs I think you’re mentioning the wrong user. I’m not part of npm team so I can’t merge this even though I want this feature. I haven’t see any npm member involved in discussion by far, so I wouldn’t expect this to happen any time soon.

@darcyclarke

Copy link
Copy Markdown
Contributor

@larsgw sorry for the delay here. We're going to close this as much of this code has been moved to Arborist (dedupe has been rewritten in v7)

@FranklinYu

Copy link
Copy Markdown

@darcyclarke Did you imply that NPM v7 won’t have this issue any more?

@TimDaubTimDaub mentioned this pull request Mar 15, 2022
2 tasks
antongolub pushed a commit to antongolub-forks/npm-cli that referenced this pull request May 18, 2024
🤖 I have created a release *beep* *boop*
---
## [4.0.4](npm/bin-links@v4.0.3...v4.0.4)
(2024-05-04)
### Bug Fixes
*
[`100a4b7`](npm/bin-links@100a4b7)
[npm#117](npm/bin-links#117) linting:
no-unused-vars (@lukekarrys)
### Chores
*
[`e955437`](npm/bin-links@e955437)
[npm#117](npm/bin-links#117) bump
@npmcli/template-oss to 4.22.0 (@lukekarrys)
*
[`b602aca`](npm/bin-links@b602aca)
[npm#117](npm/bin-links#117) postinstall for
dependabot template-oss PR (@lukekarrys)
*
[`955cc34`](npm/bin-links@955cc34)
[npm#116](npm/bin-links#116) bump
@npmcli/template-oss from 4.21.3 to 4.21.4 (@dependabot[bot])
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 2, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 13, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:majorbackwards-incompatible breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@larsgw@aeschright@G-Rath@FranklinYu@jpsfs@darcyclarke@zkat
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); dedupe: get deps from shrinkwrap by larsgw · Pull Request #118 · npm/cli · GitHub
Skip to content

dedupe: get deps from shrinkwrap - #118

Closed
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12
Closed

dedupe: get deps from shrinkwrap#118
larsgw wants to merge 4 commits into
npm:release-nextfrom
larsgw:patch-12

Conversation

@larsgw

Copy link
Copy Markdown
Contributor

@larsgw
larsgw requested a review from a team as a code ownerDecember 13, 2018 22:35
@larsgw

Copy link
Copy Markdown
ContributorAuthor

(Breaking) side effects include:

  • installing any package that is in package-lock.json but not in node_modules

It doesn't install packages that are only specified in package.json.

@zkatzkat added the semver:major backwards-incompatible breaking changes label Jan 7, 2019
@aeschright

Copy link
Copy Markdown
Contributor

We're going to make sure this doesn't have any unintended consequences and goes in the right direction.

@aeschrightaeschright left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'd like you to redo the test using our standard test script: see scripts/maketest -- you can give it a directory fixture and it will give you the test setup. It'll help us keep things consistent and avoid some older conventions that aren't effective.

@larsgw

larsgw commented Jan 16, 2019

Copy link
Copy Markdown
ContributorAuthor

I added the tests, and a doc change now too. I think this change is going to be pretty controversial, seeing how many people already disagree with npm install behavior, fixing incorrect package-lock.json files. The only middle way I can think of, fixing the bug and keeping the behavior, would be to somehow merge all movements in the node_modules tree with all the modules left behind in the shrinkwrap tree, but I don't know if that's feasible.

@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 6ca2f43 to 6d0cc95CompareJanuary 18, 2019 19:17
@zkat
zkatforce-pushed the release-next branch 3 times, most recently from db63b89 to b09bc8cCompareJanuary 23, 2019 18:36
@zkat
zkatforce-pushed the release-next branch 2 times, most recently from 06cdf5b to f957798CompareFebruary 20, 2019 20:42
@G-Rath

G-Rath commented May 14, 2019

Copy link
Copy Markdown
Contributor

What's the status of this? The community issue was closed, so I can't ask on that.

For me as a Windows user, with my expectation of what ddp does, I find it odd that running ddp results in a package-lock.json that will change if I then run npm i.

The core question I have is: Why is npm i able to generate a package-lock.json that includes fsevents without actually installing that package?

@FranklinYu

Copy link
Copy Markdown

@G-Rath I don’t think that’s an issue. Optional dependency should also be locked, so that when the package is installed on another platform, it won’t change the lock file; this way the lock file converges. In contrary, it doesn’t make sense to remove fsevents when it doesn’t apply to current platform.

@G-Rath

Copy link
Copy Markdown
Contributor

@FranklinYu For sure.

My question wasn't a challenge at npm i, but as a starting point to try and figure out how ddp should act, as this PR has the breaking side effect of :

installing any package that is in package-lock.json but not in node_modules

The point I was wanting to draw attention to with my question is that npm idoesn't have to install all packages to have them in the package-lock.json, and so why does ddp have to?

While I'm sure there's a good reason, in my eyes that highlights a starting point for identifying where ddp drifts away from i.

@FranklinYu

Copy link
Copy Markdown

installing any package that is in package-lock.json but not in node_modules

I think that only refers to packages that work for current platform. For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

@G-Rath

G-Rath commented Jun 10, 2019

Copy link
Copy Markdown
Contributor

For example, since fsevents doesn't work in Windows, even if it's in lock file, not in node_modules, it won't be installed by dedupe.

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Currently, if I run npm i on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get the same lock file regardless of OS.

Currently, if I run npm ddp on any computer, given the same package.json (and technically a complete freeze of time so that no new versions are released), I'll get a lock file that will differ based on what packages are supported by that OS.

Example:

Given this package.json:

{
"optionalDependencies": {
"fsevents": "^2.0.7"
}
}

Running npm i on Windows, Linux, & OSX gives you the following lock:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Running npm ddp on Windows or Linux gives you:

{
"lockfileVersion": 1
}

Running npm ddp on Mac gives you:

{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"fsevents": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz",
"integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==",
"optional": true
}
}
}

Note that nothing in node_modules has changed - the only change happening here is in the package-lock.json (aside from npm i on OSX, which of course installs fsevents)

@FranklinYu

Copy link
Copy Markdown

Which is fine, except deduperemovesfsevents from the lock file, which is my whole issue :)

Exactly. The whole point of this PR is that dedupeshould not remove fsevents.

@G-Rath

Copy link
Copy Markdown
Contributor

The whole point of this PR is that dedupe should not remove fsevents.

Exactly. I've never said otherwise; but it's good we're on the same page 🙂

My question is about the side effect:

installing any package that is in package-lock.json but not in node_modules

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From my understanding, the worst-case bare minimal solution would be to simply run npm i after npm ddp. That is of course highly inefficient, but it's a starting point - from there you can look to strip away unneeded parts from npm i until you're left w/ the minimal code required to "fix" the side effect.

That's a "fix it in post" style approach - which isn't bad; those kind of fixes are often just far more simpler & maintainable than the alternative: try to "fix" the actual root cause of the problem.

This could just be b/c I'm misunderstanding what is meant by "installing any package that is in package-lock.json but not in node_modules", or that just due to the complex nature of npm, that's just how it is, but as I've said: there is at least one way to work around it, that I don't see any major downsides to, given that it seems to give what we all agree is the least astonishing result from running such a command.

Eitherway, I'm just generally interested in understanding more about npm works that means that this is a side effect (w/o spending hours taking it apart line by line*) 😄

*: I mean, I'd love to do this, but sadly don't have the time 😂

@larsgw

Copy link
Copy Markdown
ContributorAuthor

I'm interested in why this side effect occurs, given that npm i doesn't have this side effect?

From what I remember (it's been a while), npm dedupe works like this:

  1. Read node_modules/ tree (like npm ls, and not package-lock.json like npm i)
  2. Perform deduping on the tree
  3. Write the tree to node_modules/ and package-lock.json

Since non-installed optional dependencies have no record in the node_modules/ tree they aren't included in the new package-lock.json. To fix that, I see two possibilities:

  1. Read from package-lock.json instead of node_modules. This has the added side-effect of installing (or trying to install) any packages that are in the package lock, but not in node_modules. This is because only reading the package lock can't tell you certain packages shouldn't be installed, just like only reading node_modules can't tell you if certain packages should be installed. However, that seemed like a smaller problem to me.
  2. What I mentioned above: somehow track the movements of the deduping process and merge those with the package lock, but leave the rest of the process. Way above my pay-grade as a voluntary contributor (and please don't spend time doing that yourself either).

PS: I think I thought of a third option just now, but I can't think of it anymore so I'll come back to this if the thought does.

@G-Rath

Copy link
Copy Markdown
Contributor

Ah interesting - thanks for the explanation :)

Would it be viable to try and leverage the logic that npm i uses to determine if a package should be installed or not?

That could be going in the direction you said not spend time trying to do via 2. :)

I'm also wondering if there is any to know (or get an idea of) all the possible conditions that can result in a package not being installed but being written to package-lock.json.

So far I only know of optionalDependencies, which from what happens w/ fsevents, seem to always be included in the package-lock.json, and so wouldn't be something ddp has to worry about?

I feel like this strengthens the argument that ddp should optimise based on package-lock.json: while it's annoying, if you're going to install a dependency in some situations, you should optimise for that situation, since otherwise isn't it harder/more work to safely determine what packages should be installed?

This has the added side-effect of installing (or trying to install)

To me that sounds like it'll work, depending on how the "trying" is handled; Looking at the error output from doing npm i fsevents on Windows, it's throwing EBADPLATFORM - That to me seems like an exception that'd only really be thrown in a predictable situation (unlike say EBADPERMISSIONS^).

Which I think boils back down to the first part of this comment, about how does npm i determine if a package should be installed, and can that be leveraged here?

*: Luckly, fsevents has no dependencies, and from what I've seen, it's the most common optionalDependencies out there.
^: Might not be an actual exception, but you know what I mean

@FranklinYu

Copy link
Copy Markdown

I like option 1. Package lock file should be the source of truth. Everyone knows that node_modules is transient and nobody checks it into version control.

@G-Rath

G-Rath commented Jun 11, 2019

Copy link
Copy Markdown
Contributor

Plus if you do "need" to modify anything in node_modules, you can use patch-package to do so stably.

@FranklinYu

Copy link
Copy Markdown

sane seems to have switched from fsevents package to fs.watch(). For future testers, we have other examples:

Both depend on fsevents for now.

@jpsfs

jpsfs commented May 6, 2020

Copy link
Copy Markdown

@FranklinYu any update on this?

@FranklinYu

Copy link
Copy Markdown

@jpsfs I think you’re mentioning the wrong user. I’m not part of npm team so I can’t merge this even though I want this feature. I haven’t see any npm member involved in discussion by far, so I wouldn’t expect this to happen any time soon.

@darcyclarke

Copy link
Copy Markdown
Contributor

@larsgw sorry for the delay here. We're going to close this as much of this code has been moved to Arborist (dedupe has been rewritten in v7)

@FranklinYu

Copy link
Copy Markdown

@darcyclarke Did you imply that NPM v7 won’t have this issue any more?

@TimDaubTimDaub mentioned this pull request Mar 15, 2022
2 tasks
antongolub pushed a commit to antongolub-forks/npm-cli that referenced this pull request May 18, 2024
🤖 I have created a release *beep* *boop*
---
## [4.0.4](npm/bin-links@v4.0.3...v4.0.4)
(2024-05-04)
### Bug Fixes
*
[`100a4b7`](npm/bin-links@100a4b7)
[npm#117](npm/bin-links#117) linting:
no-unused-vars (@lukekarrys)
### Chores
*
[`e955437`](npm/bin-links@e955437)
[npm#117](npm/bin-links#117) bump
@npmcli/template-oss to 4.22.0 (@lukekarrys)
*
[`b602aca`](npm/bin-links@b602aca)
[npm#117](npm/bin-links#117) postinstall for
dependabot template-oss PR (@lukekarrys)
*
[`955cc34`](npm/bin-links@955cc34)
[npm#116](npm/bin-links#116) bump
@npmcli/template-oss from 4.21.3 to 4.21.4 (@dependabot[bot])
---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 2, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Jul 13, 2026
github-actionsBot added a commit to Kevinlee7250/cli that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:majorbackwards-incompatible breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@larsgw@aeschright@G-Rath@FranklinYu@jpsfs@darcyclarke@zkat