I have a project that I work on with two different laptops. Sometimes I add extra packages to my project, so I have to use npm install <package-name> (duh). When I do that, I git push up the new package.json and package-lock.json files, and when I switch computers I have to git pull those changes, then run npm install again to get that package onto the other computer.
I recently noticed and started caring that one laptop kept adding carets (^) to the beginning of every package version number. For example:
One computer set package version #s to look like this:
"regexpu-core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
"integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
"requires": {
"regenerate": "1.4.0",
"regjsgen": "0.2.0",
"regjsparser": "0.1.5"
}
},
The other set package version #s to look like this:
"regexpu-core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
"integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
"requires": {
"regenerate": "^1.2.1",
"regjsgen": "^0.2.0",
"regjsparser": "^0.1.4"
}
},
I understand that carets (^) mean the version is not 100% precise, but I'm trying to figure out WHY my different laptops create different formats for package versions! I checked this SO question which has some great explanations for the differences between ~ and ^, but I didn't find anything explaining why npm would sometimes add and sometimes remove carets (^) altogether. I also looked at this npm issue on Github which recommended looking at npm config settings, but both of my laptops have the same settings:
npm config get save = true (both computers)
npm config get save-prefix = ^ (both computers)
npm config get save-exact = false (both computers)
One laptop was running npm version 5.6.0, but I just updated it to 6.5.0. The other computer was running version 6.4.1, but I also updated it to 6.5.0. I tried running npm install in my project on both computers, but still I find that one computer always removes ^ and the other always adds ^.
Please let me know if there's something I'm missing. Thanks for any help!
Edit: According to the discussion in issue #20434 this occurs by design using npm >=6.0.0.
Why does this happen?
#rarkins elaborately explains the reasoning for why this happens (and it’s advantages) in this comment. For convenience his comment is quoted below (verbatim):
Let's say that you use pinned versions of dependencies 'aaa', 'bbb' and 'ccc'. Let's say they each depend on 'zzz' like so:
aaa depends on zzz#^1.0.0
bbb depends on zzz#^1.1.0
ccc depends on zzz#^1.0.1
i.e. all three of them depend on a range of zzz, and not an exact version.
And let's say that the latest version of zzz is 1.5.0.
Both before and after this change, it's pretty obvious that the resolved version of zzz should be 1.5.0, so the only difference is how the package-lock.json is structured and documents this sub-dependency.
Before, the lock file would show that all three of them depend on zzz#1.5.0, and the resolved version of z is 1.5.0.
Now, it documents the actual "original" dependency versions (e.g. ^1.0.0, ^1.1.0, etc) for each dependency, but still shows the resolved version of z as 1.5.0.
Then consider what happens when zzz#1.5.1 is released:
Before, the lock file would need to update from z#1.5.0 to z#1.5.1 in all four places.
Now, the lock file only needs to update the resolved version of z to 1.5.1 while the dependencies can keep the ^1.0.0, ^1.1.0, and ^1.0.1 because they haven't changed.
As I mentioned previously in the thread, you still get the exact same node_modules in both cases. The advantages of the new approach are:
You get to see what the dependencies actually require (e.g. a range, and not an exact version). before, you could not tell if aaa actually required exactly zzz#1.5.0 or that it was instead zzz#^1.0.0.
Instead of four lines changing in the lock file, you get only one. It's less churn, and it's more clear what's happened.
As an aside, yarn uses a similar concept with yarn.lock. e.g. here's an example where #sindresorhus/is is pinned, but it's sub-dependency symbol-observable is not:
"#sindresorhus/is#0.10.0":
version "0.10.0"
resolved "https://registry.yarnpkg.com/#sindresorhus/is/-/is-0.10.0.tgz#f42dd6a9d12cd79fa6f53b27cf5bea3a30d2cafa"
dependencies:
symbol-observable "^1.2.0"
Original Answer:
After you git pull the revised package.json and package-lock.json onto computer two try deleting the node_modules directory before installing the packages again.
For example:
Firstly cd to your project directory on computer 2.
Delete the existing node_modules directory by running: rm -rf node_modules.
Then run: npm install
Or you can chain the two aforementioned commands using the && operator:
rm -rf node_modules && npm install
Related
We have a private JFrog artifactory (name anonymised below) that npm is configured in a project root .npmrc -file:
registry=https://artifactory.jfrog.private.com:443/api/npm/npm-registry-virtual/
The resolved-field in the package-lock.json file shared via Git between developers is constantly changing between runs of "npm install" without any changes to package.json.
Some times a dl query parameter (pointing to the exactly same URL) gets added to the resolved URL:
- "resolved": "https://artifactory.jfrog.private.com:443/api/npm/npm-registry-virtual/#sailshq/lodash/-/lodash-3.10.3.tgz",
+ "resolved": "https://artifactory.jfrog.private.com:443/api/npm/npm-registry-virtual/#sailshq/lodash/-/lodash-3.10.3.tgz?dl=https://artifactory.jfrog.private.com/#sailshq/lodash/-/lodash-3.10.3.tgz",
Some times the query parameter points to npmjs.org registry:
- "resolved": "https://artifactory.jfrog.private.com:443/api/npm/npm-registry-virtual/aproba/-/aproba-1.2.0.tgz",
- "resolved": "https://artifactory.jfrog.private.com:443/api/npm/npm-registry-virtual/aproba/-/aproba-1.2.0.tgz?dl=https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
And some times the field points directly to npmjs.org repository:
- "resolved": "https://artifactory.jfrog.private.com:443/api/npm/npm-registry-virtual/acorn/-/acorn-3.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
Any of these changes may also go to the inverse direction.
This is really irritating, since it means we constantly have meaningless changes in package-lock.json, which causes merge conflicts and often prevents npm ci from executing correctly. npm cache clean --force does not seem to help. I know that npm install can resolve package-lock.json merge conflicts automatically, but that does not help with npm ci (since the whole point is to not run npm install in the CI environment). And, anyway, what is the benefit of seeing how the virtual npm registry resolves the packages internally (as I suspect is happening here)?
Is there some kind of configuration option to prevent JFrog Artifactory from making these kinds of changes to the resolved package URLs in a virtual npm registry? Or is it maybe a bug in npm?
Environment:
npm 6.11.3
JFrog Artifactory 6.10.6
I don't know why those alternate URLs are appearing or how to make them stop. But you can reduce (or maybe even eliminate!) the merge conflict pain for your developers by using npm-merge-driver. It was written by one of the devs who was employed on the npm cli team for years, and its sole purpose is to automate away package-lock.json merge conflicts.
Our team has had success running npm ci first to ensure our locally pulled down and cached dependencies match the package-lock.json file.
Then, further npm installs should resolve as expected.
This sort of thing is normally caused by developers having slightly different versions of npm installed. Version 7 of npm just got released, so it is the perfect time to make sure the team all have exactly the same version installed.
If that doesn’t work try switching the team to yarn or pnpm.
I'm kind of lost here!
I'm using a module which has another module nested in its' node_modules.
I.E.
my_project
node_modules
widely_used_module
parent_dependency
node_modules
widely_used_module
I have some fixes in my "own" widely_used_module (it could be just a minor version from the original distributor, but to be completely honest, in this case its' my fork on Github containing some critical fixes).
When I manually remove node_modules/parent_dependency/node_modules, parent_dependency starts to reference to my "widely used module" instead of its' own. But this of course gets overriden once I hit npm install again.
Can I somehow prevent a package to install its' own modules, or can I force a package to reference the root node_modules and ignore its' own?
Is that even the right approach to fixing such issues? I don't want to fork parent_dependency as well...
Thank you
Answering my own question;
Yarn has a built-in solution for this exact issue.
This could be achievable with NPM as well but yarn made it so easy to fix that I moved the project dependencies to be handled by yarn.
Full solution:
Installing yarn
Ran yarn in project's root path
Removed package.lock.json
Added resolutions to my package.json. In my case:
{
"dependencies": {
"...": "...",
"parent_dependency": "^x.y.z"
},
"devDependencies": {
"...": "..."
},
"resolutions": {
"parent_dependency/widely_used_module": "git+https://git#github.com/myuser/widely_used_module.git"
}
}
Ran yarn install.
Result: No more widely_used_module folder under parent_dependency.
I use yarn to install packages directly from the companies' GitLab:
yarn add git+ssh://<user>#<host>:<repo>
For first-level dependencies, I use yarn --pure-lockfile to reconstruct my node_modules according to the yarn.lock.
However, for second level dependencies yarn seems to always install the latest version.
So let's say I depend on A which is tested with a specific version of B. In A's package.json I don't specify the version, but it is contained in the yarn.lock.
When I now install package A yarn will get the latest version of B despite the entry in A/yarn.lock
I know that I might resolve this by passing a specific version in A/package.json (at least I think).
But is there an option to tell yarn to look at the yarn.lock of dependencies?
TLDR:
When you install dependencies in your
application, only your own yarn.lock file is respected. Lockfiles
within your dependencies will be ignored. Reference
Let's get some things cleared first:
--pure-lockfile is same as normal yarn install except that it won't generate a yarn.lock file or update one if present.
Yarn always reads from the yarn.lock by default for resolving dependencies while installing unless supplied with --no-lockfile. So, there is no need to tell it to read from yarn.lock.
What is yarn.lock used for?
yarn.lock is used for resolving what version should be fetched
given the semver version of a module in package.json. It is not used to determine what semver version should a module be resolved to. That is simply not its use-case.
As mentioned in yarn DOCS: In order to get consistent
installs across machines, Yarn needs more information than the
dependencies you configure in your package json.. Yarn needs to store
exactly which versions of each dependency were installed.
To do this Yarn uses a yarn.lock file in the root of your project.
So, for resolving semver version of a dependency, yarn always depends on package.json. For a given semver version, yarn checks the yarn.lock file to see what version should it fetch. This is what makes yarn Deterministic (Same tecknique is used by npm which uses npm-shrinkwrap.json).
Example: Semver Versions like ^1.2.4 can resolve to any version number which is >= 1.2.3 and < 2.0.0. Without yarn, npm would install 1.2.4 in one machine but 1.9.9 in some other machine, depending on the latest version present at the time of install. This is the problem that yarn solves using yarn.lock.
The semver version is determined by the
package.json file. The yarn.lock file is only a lookup for which
version and commit hash to install for the given semver version number.
How does yarn resolve version of a module given its semver version?
Suppose currently our yarn.lock file looks like this:
bluebird#2.9.6:
version "2.9.6"
resolved "https://<...>/bluebird-2.9.6.tgz#1fc3a6b1685267dc121b5ec89b32ce069d81ab7d"
bluebird#^2.9.30:
version "2.11.0"
resolved "https://<...>/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
...
myModule#5.1.0:
version "5.1.0"
resolved "https://<...>/moduleA-5.1.0.tgz#ce97130858add59d616ee80675383b0c127290a0"
dependencies:
bluebird "^1.0.0"
If package.json has bluebird: "^2.9.30", yarn looks for an entry bluebird#^2.9.30 in the lockfile. It is present and hence is resolved to version=2.11.0.
If package.json has bluebird: "^2.9.0", yarn looks for an entry bluebird#^2.9.0 in the lockfile. It is not present. Suppose the latest stable version which satisfies semver criteria is 2.13.0, then yarn adds an entry for bluebird#^2.9.0, resolved to 2.13.0. While resolving version for a given semver version of bluebird, it does not matter what entry is present for bluebird in moduleA's dependencies in the lockfile.
Semver Version is not affected by what entries are
present in the dependencies map for a module in yarn.lock file.
So, if package.json has bluebird: "", yarn looks for an entry bluebird# in the lockfile but is unable to find it. Hence, it resolves bluebird: "" to the latest version, suppose 3.5.0. Now, yarn will add an entry for bluebird# resolved to 3.5.0.
bluebird#:
version "3.5.0"
resolved "https://<...>/bluebird-3.5.0.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
Form now on whenever yarn encounters {bluebird: ""}, it will find an entry for bluebird# in the lockfile and hence will always resolve it to 3.5.0.
Solution to your problem
To resolve B: "" to version say 1.0.0, you need to have an entry for B# in yarn.lock resolved to 1.0.0. Once, yarn.lock has an entry for B#, all the successive installs will always fetch version 1.0.0 for B="".
Following are the steps you need to take to accomplish the same:
Approach 1 (Recommended)
If you want B to resolve to latest version:
Add B:"" in A's package.json
Run yarn install. This will add an entry for B# resolved to latest version.
Push yarn.lock file.
Form now on, whoever runs yarn install will get the same version.
Approach 2
If you want B to have an older version: (Highly Not Recommended)
Add B: 1.0.0 in A's package.json.
Run yarn install. This will add an entry B#1.0.0 in the lockfile.
Add B# alongside B#1.0.0 in yarn.lock. B#, B#1.0.0: ...
Change B's version to "" in A's package.json.
Push yarn.lock file.
Form now on, whoever runs yarn install will get the B's version as 1.0.0.
This approach is highly dangerous as you can break something easily. Your yarn.lock file should always be managed by yarn.
Approach 3 (Recommended)
If you want B to stay at 1.0.0
Fix B's version to 1.0.0 in A's package.json.
Run yarn install. This will add an entry B#1.0.0 in the lockfile.
Push yarn.lock file
Form now on, whoever runs yarn install will get the B's version as 1.0.0.
Edit: Using the yarn.lock file present in the dependencies
If you check this doc:, they have clearly mentioned that yarn will use only the top level yarn.lock file and ignore the lock files present in the dependencies.
There is currently no way of locking down second level dependencies using yarn.lock present in them. I don’t see any need for it. In fact the creators of yarn explain here why that is the case. The reasons being:
The versions to be installed for second level dependencies can be
captured well by the top-level yarn.lock file, as I have explained
above.
You would never be able to update the versions of sub-dependencies in your own application when using them directly because they would be locked by other yarn.lock files. You can verify this point by my explanation of how yarn resolves dependencies.
Yarn would never be able to fold (de-duplicate) dependencies so that compatible version ranges only install a single version.
Also, as in your use-case, if A has a dependency B which works only with version 1.0.0, A’s package.json should have version mentioned for B as 1.0.0 and not “”. You can always fix your top-level yarn.lock to add an entry for B# resolved to 1.0.0 but it is not recommended to manually fix a yarn.lock file as I have mentioned above.
Hope this helped! Please ping me in the comments for any doubts.
With the release of npm#5, it will now write a package-lock.json unless a npm-shrinkwrap.json already exists.
I installed npm#5 globally via:
npm install npm#5 -g
And now, if a npm-shrinkwrap.json is found during:
npm install
a warning will be printed:
npm WARN read-shrinkwrap This version of npm
is compatible with lockfileVersion#1,
but npm-shrinkwrap.json was generated for lockfileVersion#0.
I'll try to do my best with it!
So my take-away is that I should replace the shrinkwrap with the package-lock.json.
Yet why is there a new format for it? What can the package-lock.json do that the npm-shrinkwrap.json cannot?
The files have exactly the same content, but there are a handful of differences in how npm handles them, most of which are noted on the docs pages for package-lock.json and npm-shrinkwrap.json:
package-lock.json is never published to npm, whereas npm-shrinkwrap is by default
package-lock.json files that are not in the top-level package are ignored, but shrinkwrap files belonging to dependencies are respected
npm-shrinkwrap.json is backwards-compatible with npm versions 2, 3, and 4, whereas package-lock.json is only recognized by npm 5+
You can convert an existing package-lock.json to an npm-shrinkwrap.json by running npm shrinkwrap.
Thus:
If you are not publishing your package to npm, the choice between these two files is of little consequence. You may wish to use package-lock.json because it is the default and its name is clearer to npm beginners; alternatively, you may wish to use npm-shrinkwrap.json for backwards compatibility with npm 2-4 if it is difficult for you to ensure everyone on your development team is on npm 5+. (Note that npm 5 was released on 25th May 2017; backwards compatibility will become less and less important the further we get from that date, as most people will eventually upgrade.)
If you are publishing your package to npm, you have a choice between:
using a package-lock.json to record exactly which versions of dependencies you installed, but allowing people installing your package to use any version of the dependencies that is compatible with the version ranges dictated by your package.json, or
using an npm-shrinkwrap.json to guarantee that everyone who installs your package gets exactly the same version of all dependencies
The official view described in the docs is that option 1 should be used for libraries (presumably in order to reduce the amount of package duplication caused when lots of a package's dependencies all depend on slightly different versions of the same secondary dependency), but that option 2 might be reasonable for executables that are going to be installed globally.
Explanation from NPM Developer:
The idea is definitely for package-lock.json to be the Latest and
Greatest in shrinkwrap technology, and npm-shrinkwrap.json to be
reserved for those precious few folks out there who care very much
about their libraries having an exact node_modules -- and for people
who want CI using npm#>=2 to install a particular tree without having
to bump its npm version.
The new lockfile ("package-lock.json") shares basically all of the
same code, the exact same format as npm-shrinkwrap (you can rename
them between one another!). It's also something the community seems to
understand: "it has a lockfile" seems to click so much faster with
people. Finally, having a new file meant that we could have relatively
low-risk backwards-compat with shrinkwrap without having to do weird
things like allow-publication mentioned in the parent post.
I think the idea was to have --save and shrinkwrap happen by default but avoid any potential issues with a shrinkwrap happening where it wasn't wanted. So, they just gave it a new file name to avoid any conflicts. Someone from npm explained it more thoroughly here:
https://www.reddit.com/r/javascript/comments/6dgnnq/npm_v500_released_save_by_default_lockfile_better/di3mjuk/
The relevant quote:
npm publishes most files in your source directory by default, and
people have been publishing shrinkwraps for years. We didn't want to
break compatibility. With --save and shrinkwrap by default, there was
a great risk of it accidentally making it in and propagating through
the registry, and basically render our ability to update deps and
dedupe... null.
So we chose a new name. And we chose a new name kind of all of a
sudden. The new lockfile shares basically all of the same code, the
exact same format
package-lock.json versions are guaranteed with only npm ci (since npm install overwrites package-lock.json if there is a conflict with package.json).
npm-shrinkwrap.json versions are guaranteed with both npm ci and npm install.
I have an npm package with a fixed version that has an update.
Example package.json extract:
devDependencies: {
"someFixedVersionPackage": "1.0.0", //1.1.0 is latest
"anotherFixedVersionPackage": "2.3.2", //2.3.4 is latest
}
Does an npm command exist which installs the latest version of that package and updates the package.json, preferably all packages at once?
To be clear, I want the package.json snippet above to be updated to this, in addition to the packages themselves being updated:
devDependencies: {
"someFixedVersionPackage": "1.1.0", //latest
"anotherFixedVersionPackage": "2.3.4", //latest
}
Thank you.
Why doesn't npm update work here?
As per the documentation on npm update:
This command will update all the packages listed to the latest version (specified by the tag config), respecting semver.
It will also install missing packages. As with all commands that install packages, the --dev flag will cause devDependencies to be processed as well.
Since your packages are defined with a fixed version, the update sub-command will not update those to respect semantic versioning. Therefore, it will only automatically update your packages if you specify a greater version range for each package. Note that it is actually typical in an npm project to specify a loose range version; one that is meant to avoid breaking changes but still leaves room for improvements and fixes.
Still, why shouldn't I fix dependency versions in my package.json?
But they are fixed because I wanted them so. After testing newer versions, I want to update them via command line as were created.
Having a list of dependencies with a fixed version does not mean that the dependencies installed will always be the same, because the dependencies of your dependencies will most likely also be defined with a version range. In order to keep track of a list of tested version-tagged dependencies, npm provides another mechanism: package locks.
Before version 5 of npm, you can create a "npm-shrinkwrap.json" file with the shrinkwrap command:
npm shrinkwrap
This command locks down the versions of a package's dependencies so that you can control exactly which versions of each dependency will be used when your package is installed.
Since npm 5, a "package-lock.json" is automatically generated when an npm operation modifies the "node_modules" tree or "package.json".
Rather than modifying package.json, either one of these package locks will override the default behaviour of npm install, installing dependencies with the versions specified by the lock, right when they were created or manually updated. With that out of the way, your dependencies can now be expanded without the risk of dependents installing untested package versions.
Shrinkwraps are used for publishing packages. To shrinkwrap a package:
Run npm install in the package root to install the current versions of all dependencies.
Validate that the package works as expected with these versions.
Run npm shrinkwrap, add npm-shrinkwrap.json to git, and publish your package.
At this point, dependency versions can be loosened in your package.json (this will hopefully be done only once every major dependency update), so that later on they can be updated at will with npm update:
"devDependencies": {
"someFixedVersionPackage": "^1.0.0",
"anotherFixedVersionPackage": "^2.3.2",
}
The package-lock.json file can be used instead of a shrinkwrap, and is more suitable for reproducing a development environment. It should also be committed to the repository.
So how do I update my dependencies?
Calling npm update will do what's mentioned above: update dependencies while respecting semantic versioning. To add or upgrade a dependency in a package:
Run npm install in the package root to install the current versions of all dependencies.
Add or update dependencies. npm install --save each new or updated package individually to update the package.json, as well as the existing package locks ("package-lock.json" and "npm-shrinkwrap.json"). Note that they must be explicitly named in order to be installed: running npm install with no arguments will merely reproduce the locked dependencies.
Validate that the package works as expected with the new dependencies.
Commit the new package locks.
Moreover, here are a few tips for a smooth transition from a project with fixed dependencies:
If you haven't done so, expand the version range by adding a tilde (~) before the version specifier, or a caret (^). npm update will then attempt to install all patch revisions and minor revisions, respectively (major version 0 is a corner-case, see the documentation). For instance, "^1.0.0" can now be updated to "^1.1.0", and "~2.3.2" can be updated to "~2.3.4". Adding the --save or --save-dev flags will also update the "package.json" with the installed version (while keeping the previous range specifiers).
Run npm outdated to check which packages are outdated. Entries in red will be updated automatically with npm update. Other entries will require a manual intervention.
For packages with major version bumps, install that package with a version specification (e.g. npm install browserify#11.2.0 --save-dev). Further issues that may arise with the update will have to be handled manually. It usually helps to read the news feed or the release history on that package to further understand what has changed from previous versions.
This is not simple enough, is there another way to do this?
Before continuing, it is always worth mentioning that packages have a SemVer-compliant version definition for a reason. One should avoid blindly installing the latest version of every single package. Although such a full update can be done and tools are available for that, some caution is advised. For instance, you would not want to install React 15 if the remaining React components and libraries are not compatible with react#15.x.x. See also npm's blog post: Why use SemVer?
I'll take my chances. What other tools are there?
To name a few:
npm-check-updates will do what was initially asked in the question: install and update the versions of all dependencies, regardless of the given range constraint. This would be the least recommended tool for the job, however.
updtr will update dependencies one by one and roll back to the previous version if the project's tests fail, which may save time in projects with good test coverage.
npm-check provides an interactive command-line interface, which allows you to easily select which packages to update.
Is this any different with npm 5?
Since major version 5, npm will automatically create a "package-lock.json", which will fill the role of specifying the dependency tree when a shrinkwrap does not exist. A more detailed description can be found in the package-locks documentation. In general, npm-shrinkwrap.json is meant to be used when publishing, whereas package-lock.json is to be used in development. This is why you should also commit "package-lock.json" to the repository.
What about with Yarn?
Yarn, an npm-compatible dependency manager, creates a lock file automatically on use, which behaves similarly to the npm shrinkwrap. Calling yarn upgrade «package» will update one dependency to the version in the latest tag, regardless of the version range recorded in the package.json or the lock file. Using yarn upgrade-interactive also allows you to selectively upgrade packages to the latest version, not unlike npm-check.
$ yarn outdated
yarn outdated v0.16.1
Package Current Wanted Latest
babel-eslint 7.0.0 7.0.0 7.1.0
chai 3.0.0 3.0.0 3.5.0
Done in 0.84s.
$ yarn upgrade babel-eslint chai
yarn upgrade v0.16.1
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 2 new dependencies.
├─ babel-eslint#7.1.0
└─ chai#3.5.0
Running the following command will do what you want:
npm install someFixedVersionPackage#latest anotherFixedVersionPackage#latest --save-dev --save-exact
Breakdown:
npm install someFixedVersionPackage#latest will install the latest version of the package
The --save-dev flag will cause it to update the version in your package.json's devDependencies
The --save-exact flag will cause it to save a fixed version instead of a semver range operator
Link to the npm install docs
I've been looking for an easy way to update npm dependencies for a long time. Then I found this tool: https://github.com/dylang/npm-check
It shows you which dependencies are out of date in a nice ui and allows you to update them. It even tells you which ones are likely to break due to major changes and warns you of unused dependencies.