928

I just recently upgraded to npm@5. I now have a package-lock.json file with everything from package.json. I would expect that, when I run npm install that the dependency versions would be pulled from the lock file to determine what should be installed in my node_modules directory. What's strange is that it actually ends up modifying and rewriting my package-lock.json file.

For example, the lock file had typescript specified to be at version 2.1.6. Then, after the npm install command, the version was changed to 2.4.1. That seems to defeat the whole purpose of a lock file.

What am I missing? How do I get npm to actually respect my lock file?

falinsky
  • 7,229
  • 3
  • 32
  • 56
Viper Bailey
  • 11,518
  • 5
  • 22
  • 33
  • 9
    The same problem but using yarn https://github.com/yarnpkg/yarn/issues/570 (very instructive) – Yves M. Jul 11 '17 at 14:02
  • 5
    I am having the same issue. My `package-lock.json` gets regenerated when i run `npm install`. This smells like a npm bug. Do you use your own registry? – HaNdTriX Jul 13 '17 at 13:34
  • 3
    See also [npm5 equivalent to yarn's --pure-lockfile flag?](https://stackoverflow.com/q/44800747/1480391) – Yves M. Jul 22 '17 at 21:20
  • 2
    @YvesM. `--no-save` prevents changing the lockfile, but it doesn't affect the goofy first-level dependency upgrading that the OP mentions. – Ross Allen Aug 04 '17 at 17:16
  • This seems to also happen with npm6 - I ran `npm i` without changing anything, and my `package-lock.json` was modified (the versions under all the packages in `requires` changed). It seems intended and not to break anything? More info [here](https://github.com/npm/npm/issues/20434#issuecomment-385695744) – Kevin Doyon May 01 '18 at 15:19
  • Since versions changed, that should be reflected in package-lock as well – Bernardo Dal Corno Jul 20 '18 at 02:54

12 Answers12

617

Update 3: As other answers point out as well, the npm ci command got introduced in npm 5.7.0 as additional way to achieve fast and reproducible builds in the CI context. See the documentation and npm blog for further information.


Update 2: The issue to update and clarify the documentation is GitHub issue #18103.


Update 1: The behaviour that was described below got fixed in npm 5.4.2: the currently intended behaviour is outlined in GitHub issue #17979.


Original answer (pre-5.4.2): The behaviour of package-lock.json was changed in npm 5.1.0 as discussed in issue #16866. The behaviour that you observe is apparently intended by npm as of version 5.1.0.

That means that package.json can override package-lock.json whenever a newer version is found for a dependency in package.json. If you want to pin your dependencies effectively, you now must specify the versions without a prefix, e.g., you need to write them as 1.2.0 instead of ~1.2.0 or ^1.2.0. Then the combination of package.json and package-lock.json will yield reproducible builds. To be clear: package-lock.json alone no longer locks the root level dependencies!

Whether this design decision was good or not is arguable, there is an ongoing discussion resulting from this confusion on GitHub in issue #17979. (In my eyes it is a questionable decision; at least the name lock doesn't hold true any longer.)

One more side note: there is also a restriction for registries that don’t support immutable packages, such as when you pull packages directly from GitHub instead of npmjs.org. See this documentation of package locks for further explanation.

SamB
  • 9,039
  • 5
  • 49
  • 56
jotaen
  • 6,739
  • 1
  • 11
  • 24
  • It's a long discussion, so I wouldn't be surprised if the behaviour changed in future. For now, [this comment](https://github.com/npm/npm/issues/17979#issuecomment-321465530) also confirms your advice, that versions in `package.json` must also be locked. – joeytwiddle Aug 11 '17 at 09:41
  • 1
    By the way, once you have locked everything, if you do want to try out newer versions of your dependencies, there is a nice tool called [updtr](https://www.npmjs.com/package/updtr) which can help with that. – joeytwiddle Aug 11 '17 at 09:44
  • 80
    What the hack is `npm update` for then ? :o I've had same feeling that `npm install` updated deps, but I doesn't want to believe it.. but seems like it's sadly true.. Anyway there is still option to use `npm shrinkwrap` to lock deps, but definitely name package-lock is incorrect as it does not freeze, nor lock dependencies.. – Jurosh Sep 07 '17 at 06:48
  • 374
    What a mess! The worlds largest package manager yet it doesn't have documentation on how it should work. Everyone is guessing about what it should do and it turns into a war of opinions. Discussion is good but should happen prior to a release into the wild. At some point someone needs to make the final call and then in can get implemented, documented and released. PHP was designed by committee and ad-hoc'd together and look how it turned out. I'd hate to see the same thing happen to a tool this critical and widely used. – Landon Poch Sep 15 '17 at 07:45
  • 121
    Then, what is the point of using package-lock ? I thought it would create the same environment in different workspaces but turns out it is doing nothing – laltin Oct 30 '17 at 20:32
  • 2
    I'm really confused... So, 1) how should I actually deploy exactly locked package tree to staging? just "npm i --no-save"? 2) how should I update packages in dev for preparing new version? "npm up --save"? Honestly I expected that there would be a separate command that produces the tree from package-lock.json file – Dmitry Gusarov Nov 11 '17 at 10:13
  • I'm still waiting for cipm to get released but there isn't much transparency around when we can expect that. Sounds like the npm folks are developing cipm specifically for regenerating the dependencies based solely off the lock file. – Landon Poch Dec 04 '17 at 18:06
  • 31
    "Then the combination of package.json and package-lock.json will yield reproducible builds." What role does "package-lock.json" have here? Doesn't "package.json" alone already yield reproducible builds if no version prefixes are used? – Jānis Elmeris Feb 27 '18 at 11:46
  • 17
    @JānisElmeris I think package.json can't lock down deep dependencies... – Ruan Mendes Dec 11 '18 at 20:30
  • 6
    yarn does not have this problem. It installs exactly what is listed in package-lock. – Justin Meiners Feb 27 '19 at 17:23
  • 1
    Can confirm, `yarn.lock` doesn't not seem to suffer the same issue as `package-lock.json`. I would use it over npm where possible. – Dan Aug 02 '19 at 22:27
  • 14
    @LandonPoch Not sure what your comment on PHP is in reference to, but Composer is a *way* better package manager than NPM (and yarn). Sounds like you may not have checked out PHP in awhile. – Ascherer Aug 20 '19 at 20:02
  • @Ascherer, that comment refers to PHP the language, not Composer. PHP has a horribly inconsistent, dangerous, and unintuitive standard API and utterly redundant functions all over the place. – Peter Sep 11 '19 at 11:07
  • @Dan, @JustinMeiners: Please note in answer *"Update 1: The behaviour that was described below got fixed in npm 5.4.2"*. To summarize: Once lock file has been generated, it *does* now avoid updating to newer versions - *until you edit package.json to specify a different version or range*. As of 5.4+, `npm install` no longer takes the newest available package, if the "locked" version is consistent with what package.json specifies. – ToolmakerSteve Oct 30 '19 at 20:48
  • 2
    Does that mean that the behaviour of `npm ci` is now similar to that of `npm i` - at least in terms of modifications of the package-lock.json when package.json is unchanged? – Peter Chaula Dec 11 '19 at 10:40
  • @jotaen I have a package in package.json file: "dependencies": { "abcd": "^1.0.66", } . And this package is locked in package-lock.json file: "abcd": { "version": "1.0.66", } When I execute `npm install` then a new package gets installed of version `1.0.71`. This is happening because of the caret sign or due to the version of npm which is `5.5.1.` or both? Please confirm. My understanding is `npm install` should pick the locked version `1.0.66` rather than `1.0.71`. – NN796 Mar 29 '20 at 10:37
  • Please provide some suggestion on this: https://stackoverflow.com/questions/61019398/why-does-npm-install-git-repo-url-rewrite-package-lock-json – NN796 Apr 03 '20 at 20:19
  • 3
    @LandonPoch that same committee is responsible for PHP7 which is a massive step forward for PHP. –  Oct 14 '20 at 12:14
  • 1
    I'm not sure about the `npm ci` advice. I give it myself, but I am second guessing. The issue is that it will redownload and install everything anew. This can be more time than `npm install`! Good security feature, bad for development – George Mauer Apr 14 '21 at 22:16
  • The last comment in GitHub issue #17979 claims this behavior was fixed in 5.4.2. It's still broken in all of 6.X, and not once did I see it work as described in 5.X either. – Jason Jun 09 '22 at 03:37
  • @Jason yes I confirm npm ci delete all node_modules but did not installed new one, and even if was doing, is not really a good think is depend on the semantic of package-lock.json that can be huge. – Carmine Tambascia Aug 30 '22 at 16:21
245

I've found that there will be a new version of npm 5.7.1 with the new command npm ci, that will install from package-lock.json only

The new npm ci command installs from your lock-file ONLY. If your package.json and your lock-file are out of sync then it will report an error.

It works by throwing away your node_modules and recreating it from scratch.

Beyond guaranteeing you that you'll only get what is in your lock-file it's also much faster (2x-10x!) than npm install when you don't start with a node_modules.

As you may take from the name, we expect it to be a big boon to continuous integration environments. We also expect that folks who do production deploys from git tags will see major gains.

Community
  • 1
  • 1
Ivan Shcherbakov
  • 2,633
  • 2
  • 15
  • 10
  • 215
    This should be the default behavior if a lockfile exists. – nullability Jul 12 '18 at 16:48
  • 19
    So they changed how npm i works, only to bring it back in as npm ci months later? – Scott Flack Jul 13 '18 at 00:32
  • Not all projects have a lock file, so I suppose the best way to initialize an arbitrary repo you've downloaded is `npm ci || npm i` – Qwertie Jul 13 '18 at 20:44
  • 6
    I'm still confused. The documentations says *"Make sure you have a package-lock and an up-to-date install: `npm install`"* before running the command `npm ci` in that project. Doesn't `npm install` overwrite the package-lock.json file? – adiga Jun 22 '19 at 10:41
  • 4
    AFAIK: @adiga - starting with version 5.4, `npm` *only* changes the lock file *if necessary to do so, to meet the spec in packages.json*. So if packages used to say `thatpackage: 1`, and lock says `..: 1.0.4`, dev can edit to say `thatpackage: 2` - and that will force lock file to change, because `1.0.4` isn't compatible with the newly specified range. If don't change `packages.json`, will stay locked at exact version, until delete lock file. [If doesn't stay locked, and didn't change packages.json, file a bug report.] – ToolmakerSteve Oct 30 '19 at 20:56
  • 2
    Spent my entire day on it. I spent my entire day on this fundamental issue :( :( – Omar Tariq Nov 07 '19 at 15:52
  • @nullability "This should be the default behavior if a lockfile exists." As ToolmakerSteve explains, latest npm *does install only from the lockfile*, if one exists, and the versions it contains are valid for the ranges specified in `package.json`. So for the regular use-case of cloning a repo and running `npm install`, the `npm install` command *does* only use the `package-lock.json` contents. (it only uses `package.json` ranges for modules where the `package-lock.json` version is invalid for the range) – Venryx Nov 13 '19 at 10:38
  • @venryx so if a package-lock.json exists the only time `npm install` will use package.json is if package.json has been changed since the package-lock.json was generated? Meaning unless someone checked in an updated package.json without also checking in the package-lock.json , `npm install` will result an unmodified package-lock.json file ? – George Nov 19 '19 at 18:15
  • 1
    @George From the information I've read (for recent versions of npm), and my limited testing: yes to both. – Venryx Nov 19 '19 at 23:55
  • Is there a similar command for NPM version 3 ? I see npm ci is introduced in NPM version 5. – Manoj Bharadwaj Sep 15 '20 at 18:47
  • This answer explains the differences and when to use each: https://stackoverflow.com/a/64014814/10788155 – Ictus Aug 21 '22 at 10:19
  • I was really suffering, this fixed it. Thanks. – droid-zilla Dec 23 '22 at 07:07
174

Short Answer:

  • npm install honors package-lock.json only if it satisfies the requirements of package.json.
  • If it doesn't satisfy those requirements, packages are updated & package-lock is overwritten.
  • If you want the install to fail instead of overwriting package-lock when this happens, use npm ci.

Here is a scenario that might explain things (Verified with NPM 6.3.0)

You declare a dependency in package.json like:

"depA": "^1.0.0"

Then you do, npm install which will generate a package-lock.json with:

"depA": "1.0.0"

Few days later, a newer minor version of "depA" is released, say "1.1.0", then the following holds true:

npm ci       # respects only package-lock.json and installs 1.0.0

npm install  # also, respects the package-lock version and keeps 1.0.0 installed 
             # (i.e. when package-lock.json exists, it overrules package.json)

Next, you manually update your package.json to:

"depA": "^1.1.0"

Then rerun:

npm ci      # will try to honor package-lock which says 1.0.0
            # but that does not satisfy package.json requirement of "^1.1.0" 
            # so it would throw an error 

npm install # installs "1.1.0" (as required by the updated package.json)
            # also rewrites package-lock.json version to "1.1.0"
            # (i.e. when package.json is modified, it overrules the package-lock.json)
ATOMP
  • 1,311
  • 10
  • 29
Ahmad Abdelghany
  • 11,983
  • 5
  • 41
  • 36
  • 9
    This is indeed the intended behavior of a "lock" file. Apparently, it was not the case with older versions of NPM. – Blockost Jan 15 '19 at 21:57
  • 1
    Then how does npm track the last update to package.json? What happens when you move your package.json and package-lock.json to another computer? How does npm in new computer know whether package.lock is the original one or it has been updated, to decide whether it needs to update package-lock.json or not? – Lahiru Chandima Jun 08 '19 at 08:15
  • This is now befuddled by adding `npm-shrinkwrap.json` which is exactly the same in syntax and contents as `package-lock.json` and intends the same functionality. – Joe Atzberger Jun 13 '19 at 01:02
  • 9
    @LahiruChandima It does not really track updates. `npm install` will use the locked versions from `package-lock.json` unless it does not satisfy the `package.json` in which case it installs package.json and rebuilds package-lock.json accordingly. If you changed your `package.json` in such a way that existing package-lock still satisfies the updated `package.json` it will continue to use that `package-lock` – Ahmad Abdelghany Jun 28 '19 at 13:32
  • 1
    If you already have a module in node_modules that meets the requirements of package.json, then `npm install` does nothing, regardless of package-lock.json. We have to explicitly update packages even when there are updates available that match the semver specified in package.json. At least that has been my experience for years. – carlin.scott Sep 12 '19 at 18:29
  • @carlin.scott - that sounds very different from what people complained about at npm, for years. Which is why behavior was changed in 5.1, then partially changed back in 5.4. Are you sure you deleted the lock file? – ToolmakerSteve Oct 30 '19 at 21:13
  • 1
    @ToolmakerSteve I was also skeptical of the behavior @carlin.scott reported, but I just tested it, and in fact he's correct. If the version within `node_modules` satisfies the range in `package.json`, and there is no `package-lock.json` file, npm will not update the module when running `npm install`. I guess it's fine since you can use `npm update` (or `npm-check` for latest) to update dependencies, and this behavior is faster for the case of someone just adding one entry to `package.json`, and not wanting unrelated packages to update themselves to the latest that satisfies the sem-ver range. – Venryx Nov 13 '19 at 10:33
  • Does `npm install` respect all `package-lock.json` files within the whole dependency tree (not only within the top level dependencies)? – Ini Jun 27 '20 at 20:28
  • When I run `npm ci` it happily installs packages, so apparently package.json and package-lock.json are compatible. But `npm install` still rewrites package-lock.json. Seems like a counter to this answer. I'm on npm 6.14.11 and node 12.13.0. – Alex Hall Feb 04 '21 at 18:00
  • When you say "manually update", do you mean if you go in by hand and change the version, or by writing the command, ```npm i depA@^1.10```? Just want to clarify. – MattoMK Feb 25 '22 at 03:52
  • @MattoMK yes, I meant if you change the version by hand. – Ahmad Abdelghany Mar 01 '22 at 11:04
  • Yeah except it doesn't do that. What it does is constantly get confused about whether the package.json has actually been updated. Which is why people keep asking what's the deal with lockfiles in node. – Jason Jun 09 '22 at 03:31
  • It's a bit unsettling that npm install does not work with the package-lock file out of the box. This is what the developer would expect when having both a package.json and package-lock.json in the repository. – Greg Tomasik May 18 '23 at 11:46
120

Use the newly introduced

npm ci

npm ci promises the most benefit to large teams. Giving developers the ability to “sign off” on a package lock promotes more efficient collaboration across large teams, and the ability to install exactly what is in a lockfile has the potential to save tens if not hundreds of developer hours a month, freeing teams up to spend more time building and shipping amazing things.

Introducing npm ci for faster, more reliable builds

Gal Margalit
  • 5,525
  • 6
  • 52
  • 56
  • 5
    this seems to be correct to me? can anyone else confirm? – phouse512 Jun 28 '18 at 20:22
  • 9
    @phouse512 This is correct. We pretty much _only_ use `npm ci`, and only use `npm install` if updating or installing new packages. – Jacob Sievers Aug 06 '18 at 09:08
  • 1
    Recent comments, etc. This is the answer I'm going with. Too bad they coudln't fix the horrible snafu, but if the new gospel is "npm ci", then fine. I can adapt. – Svend Aug 14 '18 at 11:12
  • 2
    Too bad it *always* deletes an existing `node_modules` directory and rebuilds locally, even if that is an otherwise empty but important symlink. :( – Joe Atzberger Jun 13 '19 at 00:57
  • @JoeAtzberger - sounds like that is worth opening an issue at npm. It really should just delete all *contents* of directory. – ToolmakerSteve Oct 30 '19 at 21:09
  • 2
    @ToolmakerSteve Don't hold your breath! I think deleting the contents of a directory would be magnitudes slower than just deleting the directory. You would have to enumerate the contents then issue a series of delete commands rather than just the one delete command to the O/S. With the performance issues previously levelled at npm and the improvement using `npm ci` I expect they would be very reluctant to introduce anything that could reduce performance for a fairly uncommon use case. You might want to check out https://pnpm.js.org/ though that makes use of hard links to reduce disk usage. – Caltor Dec 19 '19 at 10:25
  • I would also say that it should have a flag to not do this at all and to simply trust that your contents haven't been manipulated. Redownloading everything sucks so bad when yiou're in a dev workflow – George Mauer Apr 14 '21 at 22:17
  • @JoeAtzberger Maybe chmod 555 your symlink? – Nicholas Shanks Jun 13 '22 at 10:51
39

Use the npm ci command instead of npm install.

"ci" stands for "clean install".

It will install the project dependencies based on the package-lock.json file instead of the lenient package.json file dependencies.

It will produce identical builds to your team mates and it is also much faster.

You can read more about it in this blog post: https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable

Daniel Tonon
  • 9,261
  • 5
  • 61
  • 64
  • 7
    `ci` refers to "continuous integration", as mentioned in the docs and blog post announcing the command: http://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable – Joe Atzberger Jun 13 '19 at 01:04
  • Thanks Joe. I've updated my answer with the correct name and linked to the blog post. (for those reading this, previously I said that it stands for "clean install") – Daniel Tonon Jul 12 '19 at 09:03
  • 3
    "And it is also much faster" - it will delete `node_modules` folder and re-create it from scratch. Is it really much faster? Does `npm install` delete `node_modules` folder, too? – izogfif Nov 27 '19 at 11:29
  • 3
    I think the speed comes from npm not needing to calculate what packages to download. Think of it like `npm install` has to resolve all the package dependencies when run. `npm ci` is just a shopping list of "get these exact modules". – Daniel Tonon Nov 27 '19 at 11:58
  • I'm having an issue with this, and I just tried `npm ci` instead of `npm i` and it's actually not even letting me perform that task. It's telling me I need to first run `npm i` :( – Cin88 Sep 04 '20 at 13:54
  • 3
    `ci` stands for `clean install` actually. – Antoniossss Sep 16 '21 at 08:51
  • That's what I thought as well but no. `npm ci` stands for "continuous integration" according to the official blog post. See the earlier comment by Joe. – Daniel Tonon Sep 17 '21 at 05:54
  • 1
    stands for "clean install", I just read a post by the person who built it. Can't find the link at the moment.. – MattoMK Feb 25 '22 at 01:47
  • I've changed it back to "clean install" since I think it makes more sense. – Daniel Tonon Feb 26 '22 at 02:36
  • in my react native application npm ci delete all node_modules but did not installed them again. Is this a broken behavior or somehow was changed? – Carmine Tambascia Aug 30 '22 at 16:15
  • ```ci``` refer to **clean install** check the official docs here https://docs.npmjs.com/cli/v9/commands/npm-ci – Wasit Shafi Apr 14 '23 at 10:44
10

It appears this issue is fixed in npm v5.4.2

https://github.com/npm/npm/issues/17979

(Scroll down to the last comment in the thread)

Update

Actually fixed in 5.6.0. There was a cross platform bug in 5.4.2 that was causing the issue to still occur.

https://github.com/npm/npm/issues/18712

Update 2

See my answer here: https://stackoverflow.com/a/53680257/1611058

npm ci is the command you should be using when installing existing projects now.

Daniel Tonon
  • 9,261
  • 5
  • 61
  • 64
  • 7
    I'm using 5.4.2 and it's still resulting in the modification of my package-lock.json when `npm i`. For instance, the module `fsevents` is removed when I `npm i` on a machine that does not support `fsevents` and then the module is re-added when one `npm i` again on a machine that does. – hrdwdmrbl Oct 03 '17 at 22:12
  • Then you should raise a new issue in the npm GitHub repo explaining this. If it doesn't work how they say it is supposed to work then they see it as a high priority bug that urgently needs fixing. – Daniel Tonon Oct 03 '17 at 22:23
  • @hrdwdmrbl I'm seeing the same `fsevents` drop in my `package-lock.json` with `npm@5.5` while collaborating with Mac OS X contributors. If you haven't opened an issue, I will. – AL the X Nov 16 '17 at 20:48
  • @hrdwdmrbl I found that (and the long thread of associated issues) after I left my comment and forgot to come back to SO to update my comment. Thanks for getting my back. Everything is fine. – AL the X Nov 22 '17 at 03:23
9

In the future, you will be able to use a --from-lock-file (or similar) flag to install only from the package-lock.json without modifying it.

This will be useful for CI, etc. environments where reproducible builds are important.

See https://github.com/npm/npm/issues/18286 for tracking of the feature.

  • I doubt it. How if dependencies are different for different operation systems, how can you force install something that would not work? – Yevgeniy Afanasyev Jan 24 '18 at 23:27
  • 7
    @YevgeniyAfanasyev Instead of that flag, it was implemented as `npm ci` which also handles your question. – spex Mar 30 '18 at 02:45
8

Probably you should use something like this

npm ci

Instead of using npm install if you don't want to change the version of your package.

According to the official documentation, both npm install and npm ci install the dependencies which are needed for the project.

The main difference is, npm install does install the packages taking packge.json as a reference. Where in the case of npm ci, it does install the packages taking package-lock.json as a reference, making sure every time the exact package is installed.

6

You probably have something like:

"typescript":"~2.1.6"

in your package.json which npm updates to the latest minor version, in your case being 2.4.1

Edit: Question from OP

But that doesn't explain why "npm install" would change the lock file. Isn't the lock file meant to create a reproducible build? If so, regardless of the semver value, it should still use the same 2.1.6 version.

Answer:

This is intended to lock down your full dependency tree. Let's say typescript v2.4.1 requires widget ~v1.0.0. When you npm install it grabs widget v1.0.0. Later on your fellow developer (or CI build) does an npm install and gets typescript v2.4.1 but widget has been updated to widget v1.0.1. Now your node module are out of sync. This is what package-lock.json prevents.

Or more generally:

As an example, consider

package A:

{ "name": "A", "version": "0.1.0", "dependencies": { "B": "<0.1.0" } }

package B:

{ "name": "B", "version": "0.0.1", "dependencies": { "C": "<0.1.0" } }

and package C:

{ "name": "C", "version": "0.0.1" }

If these are the only versions of A, B, and C available in the registry, then a normal npm install A will install:

A@0.1.0 -- B@0.0.1 -- C@0.0.1

However, if B@0.0.2 is published, then a fresh npm install A will install:

A@0.1.0 -- B@0.0.2 -- C@0.0.1 assuming the new version did not modify B's dependencies. Of course, the new version of B could include a new version of C and any number of new dependencies. If such changes are undesirable, the author of A could specify a dependency on B@0.0.1. However, if A's author and B's author are not the same person, there's no way for A's author to say that he or she does not want to pull in newly published versions of C when B hasn't changed at all.


OP Question 2: So let me see if I understand correctly. What you're saying is that the lock file specifies the versions of the secondary dependencies, but still relies on the fuzzy matching of package.json to determine the top-level dependencies. Is that accurate?

Answer: No. package-lock locks the entire package tree, including the root packages described in package.json. If typescript is locked at 2.4.1 in your package-lock.json, it should remain that way until it is changed. And lets say tomorrow typescript releases version 2.4.2. If I checkout your branch and run npm install, npm will respect the lockfile and install 2.4.1.

More on package-lock.json:

package-lock.json is automatically generated for any operations where npm modifies either the node_modules tree, or package.json. It describes the exact tree that was generated, such that subsequent installs are able to generate identical trees, regardless of intermediate dependency updates.

This file is intended to be committed into source repositories, and serves various purposes:

Describe a single representation of a dependency tree such that teammates, deployments, and continuous integration are guaranteed to install exactly the same dependencies.

Provide a facility for users to "time-travel" to previous states of node_modules without having to commit the directory itself.

To facilitate greater visibility of tree changes through readable source control diffs.

And optimize the installation process by allowing npm to skip repeated metadata resolutions for previously-installed packages.

https://docs.npmjs.com/files/package-lock.json

Matt
  • 33,328
  • 25
  • 83
  • 97
  • 38
    But that doesn't explain why "npm install" would change the lock file. Isn't the lock file meant to create a reproducible build? If so, regardless of the semver value, it should still use the same 2.1.6 version. – Viper Bailey Jul 10 '17 at 21:53
  • [Source](https://docs.npmjs.com/files/package-lock.json) of the second quote would be nice. – noppa Jul 10 '17 at 22:40
  • So let me see if I understand correctly. What you're saying is that the lock file specifies the versions of the secondary dependencies, but still relies on the fuzzy matching of package.json to determine the top-level dependencies. Is that accurate? – Viper Bailey Jul 11 '17 at 04:33
  • 4
    And that's the thing I'm saying. My package lock file says typescript@2.1.6 but when I run npm install, the entry is replaced with typescript@2.4.1. – Viper Bailey Jul 11 '17 at 16:56
  • Hmmmm. Did you delete your packagelock? Make sure you have the latest npm ==> `$npm update -g npm` – Matt Jul 11 '17 at 17:11
  • 6
    I've experienced this same issue. In our CI/CD, the `package-lock.json` gets pulled down and then we run `npm install`, but the `package-lock.json` file is modified and we have to perform a reset before we can pull the next changes. – BayssMekanique Jul 24 '17 at 19:42
  • 1
    "...such that teammates, deployments, and continuous integration are guaranteed to install exactly the same dependencies." I don't see how this can be true if it modifies the lockfile whenever you do `npm i`? – callum Aug 03 '17 at 15:16
  • 19
    I don't get it. How is this a "lock" file if subsequent installs might still do upgrades?! – Ross Allen Aug 04 '17 at 17:17
  • 2
    For me this answer doesn’t really solve the issue. Especially the answer to OP2 I cannot reproduce; in fact npm 5.3.0 doesn’t behave like this on my machine. Say, if typescript was specified with `~2.1.6` in `package.json`, npm still ignores whatever was output in the lock file. – jotaen Aug 07 '17 at 18:59
  • 5
    I think they started with the idea of having this file as "info" and "lock" and then, decided it will be only an "info" file. Better name would be "package-info.json". I would love to have a "npm install -lock" which will install from "package-lock.json" and ignore "package.json" – Jeremy Chone Sep 07 '17 at 02:20
  • The lead paragraph of this answer is still fundamentally wrong. The question is about a situation *where the lock file exists* - which should prevent the described update from happening. – ToolmakerSteve Oct 30 '19 at 21:28
4

EDIT: the name "lock" is a tricky one, its NPM trying to catch up with Yarn. It isn't a locked file whatsoever. package.json is a user-fixed file, that once "installed" will generate node_modules folder tree and that tree will then be written in package-lock.json. So you see, its the other way around - dependency versions will be pulled from package.json as always, and package-lock.json should be called package-tree.json

(hope this made my answer clearer, after so many downvotes)


A simplistic answer: package.json have your dependencies as usual, while package-lock.json is "an exact, and more importantly reproducible node_modules tree" (taken from npm docs itself).

As for the tricky name, its NPM trying to catch up with Yarn.

Bernardo Dal Corno
  • 1,858
  • 1
  • 22
  • 27
  • 1
    Because if you run npm install, package-lock will be updated. – Jean-Baptiste Feb 19 '18 at 16:34
  • Other answers already mentioned `npm ci`. In [another answer of mine](https://stackoverflow.com/a/64014814/10788155) is explained the relationship between `npm install`, `npm ci`, `package.json`, and `package.lock.json` – Ictus Aug 21 '22 at 14:04
0

There is an open issue for this on their github page: https://github.com/npm/npm/issues/18712

This issue is most severe when developers are using different operating systems.

hrdwdmrbl
  • 4,814
  • 2
  • 32
  • 41
-4

Npm install detects any changes made to package.json file to reflect the dependency list accordingly.

Ex. If user added or removed a new dependency, the build will download or remove the dependencies in the local computer. We can compare this to .m2 repository in java where maven keeps track of pom.xml file constantly to update the dependencies.

package-lock.json is a replica of package.json used at run-time by internal processes, only difference is package-lock.json is read-only to user.

obul
  • 23
  • 3