I understood that package-lock.json is useful to block the dependencies versions in order to
Describe a single representation of a dependency tree such that teammates, deployments, and continuous integration are guaranteed to install exactly the same dependencies.
However if I run npm update, my dependencies are updated and package-lock.json versions too. So in the future, I can no longer reproduce the same original tree from package-lock.json. It doesn't serve its purpose, or am I losing something?
npm update updates the dependencies in package.json. during the update, the updated packages are being installed, thus package-lock.json is being updated.
the role of package-lock.json is to make sure that all transitive (or second-level dependencies and above) are locked. this governs that each invocation of npm install will result in installting the same packages.
Related
My project depends on analytics-node which has a dependency on axios: "^0.16.2".
We have been flagged with npm audit that axios has a vulnerability in it but its fixed in 0.18.1.
However, analytics-node has no release candidate (only beta) which depends on 0.18.1 or higher.
We have tried:
npm audit fix,
npm update axios --depth 2,
npm install axios#0.18.1
The part I am most confused about is why doesnt npm allow us to override analytics-node version of axios given that the version should be compatible.
npm-force-resolutions specifically fixes transitive dependency version resolutions for this exact reason.
It would be nicer if there was a supported way to do this with package-lock.json though.
NPM 8 introduced "overrides" which allows you to override specific transitive dependencies of your direct dependency. For your usecase, you would declare something like below in your package.json.
{
"overrides": {
"analytics-node": {
"axios": "0.18.1"
}
}
}
More details # https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides
To bump (i.e. update) a transitive dependency in a package-lock.json file, you can use the npm command line interface (CLI). To update a transitive dependency, you will first need to identify the package that you want to update. Then, you can use the following steps:
Navigate to your project directory in the terminal and run the npm ls command to list all of the dependencies in your project, including transitive dependencies. This will show you the full dependency tree, with each package and its dependencies listed in a hierarchical structure.
Find the package that you want to update in the dependency tree, and note the version number of the transitive dependency that you want to update.
Run the npm install command, followed by the name and version of the package that you want to update, in the following format: npm install <package-name>#<version>. This will update the specified package to the specified version.
Run the npm ls command again to verify that the transitive dependency has been updated. You should see the new version number for the package listed in the dependency tree.
If you want to save the updated dependency in your package-lock.json file, run the npm shrinkwrap or npm update command, depending on which version of npm you are using. This will update the package-lock.json file to reflect the updated transitive dependency.
Note: Bumping a transitive dependency in package-lock.json can potentially cause conflicts or other issues if the updated dependency is not compatible with your project's dependencies. It is recommended to carefully review the dependencies and their versions before updating any packages.
I'm in the process of switching from npm to pnpm, which has a different set of features that I prefer.
The former uses package-lock.json to lock exact packages versions, the latter pnpm-lock.yaml.
According to the docs, pnpm ignores package-lock.json, which seems to become redundant. And, as a consequence, when updating packages with pnpm, this file will not be processed at all and will soon become outdated.
Can I safely delete package-lock.json or will I incur in some problems of sort? Could there be any drawbacks in deleting this file from my project?
I'm using npm only to get the tools and packages needed for my projects (i.e. Gulp), not to publish packages to https://www.npmjs.com/
If you use pnpm, you can remove package-lock.json (and yarn.lock if you are switching from Yarn). If you need to preserve some dependencies from your old lockfile, you can run pnpm import and pnpm will generate a lockfile using info from package-lock.json.
pnpm will create a lockfile of its own. pnpm's lockfile is called pnpm-lock.yaml. You should commit this file to the repository.
If you have a CI server, you'll have to use pnpm on the CI server as well, for installing dependencies. Here are some instructions how to do that.
I'm working with continuous integration and discovered the npm ci command.
I can't figure what the advantages are of using this command for my workflow.
Is it faster? Does it make the test harder, okay, and after?
From the official documentation for npm ci:
In short, the main differences between using npm install and npm ci are:
The project must have an existing package-lock.json or npm-shrinkwrap.json.
If dependencies in the package lock do not match those in package.json, npm ci will exit with an error, instead of updating the package lock.
npm ci can only install entire projects at a time: individual dependencies cannot be added with this command.
If a node_modules is already present, it will be automatically removed before npm ci begins its install.
It will never write to package.json or any of the package-locks: installs are essentially frozen.
Essentially,
npm install reads package.json to create a list of dependencies and uses package-lock.json to inform which versions of these dependencies to install. If a dependency is not in package-lock.json it will be added by npm install.
npm ci (also known as Clean Install) is meant to be used in automated environments — such as test platforms, continuous integration, and deployment — or, any situation where you want to make sure you're doing a clean install of your dependencies.
It installs dependencies directly from package-lock.json and uses package.json only to validate that there are no mismatched versions. If any dependencies are missing or have incompatible versions, it will throw an error.
Use npm install to add new dependencies, and to update dependencies on a project. Usually, you would use it during development after pulling changes that update the list of dependencies but it may be a good idea to use npm ci in this case.
Use npm ci if you need a deterministic, repeatable build. For example during continuous integration, automated jobs, etc. and when installing dependencies for the first time, instead of npm install.
npm install
Installs a package and all its dependencies.
Dependencies are driven by npm-shrinkwrap.json and package-lock.json (in that order).
without arguments: installs dependencies of a local module.
Can install global packages.
Will install any missing dependencies in node_modules.
It may write to package.json or package-lock.json.
When used with an argument (npm i packagename) it may write to package.json to add or update the dependency.
when used without arguments, (npm i) it may write to package-lock.json to lock down the version of some dependencies if they are not already in this file.
npm ci
Requires at least npm v5.7.1.
Requires package-lock.json or npm-shrinkwrap.json to be present.
Throws an error if dependencies from these two files don't match package.json.
Removes node_modules and install all dependencies at once.
It never writes to package.json or package-lock.json.
Algorithm
While npm ci generates the entire dependency tree from package-lock.json or npm-shrinkwrap.json, npm install updates the contents of node_modules using the following algorithm (source):
load the existing node_modules tree from disk
clone the tree
fetch the package.json and assorted metadata and add it to the clone
walk the clone and add any missing dependencies
dependencies will be added as close to the top as is possible
without breaking any other modules
compare the original tree with the cloned tree and make a list of
actions to take to convert one to the other
execute all of the actions, deepest first
kinds of actions are install, update, remove and move
npm ci will delete any existing node_modules folder and relies on the package-lock.json file to install the specific version of each package. It is significantly faster than npm install because it skips some features. Its clean state install is great for ci/cd pipelines and docker builds! You also use it to install everything all at once and not specific packages.
While everyone else has answered the technical differences none explain in what situations to use both.
You should use them in different situations.
npm install is great for development and in the CI when you want to cache the node_modules directory.
When to use this? You can do this if you are making a package for other people to use (you do NOT include node_modules in such a release). Regarding the caching, be careful, if you plan to support different versions of Node.js remember that node_modules might have to be reinstalled due to differences between the Node.js runtime requirements. If you wish to stick to one version, stick to the latest LTS.
npm ci should be used when you are to test and release a production application (a final product, not to be used by other packages) since it is important that you have the installation be as deterministic as possible, this install will take longer but will ultimately make your application more reliable (you do include node_modules in such a release). Stick with LTS version of Node.js.
npm i and npm ci both utilize the npm cache if it exists, this cache lives normally at ~/.npm.
Also, npm ci respects the package-lock.json file. Unlike npm install, which rewrites the file and always installs new versions.
Bonus: You could mix them depending on how complex you want to make it. On feature branches in git you could cache the node_modules to increase your teams productivity and on the merge request and master branches rely on npm ci for a deterministic outcome.
The documentation you linked had the summary:
In short, the main differences between using npm install and npm ci are:
The project must have an existing package-lock.json or npm-shrinkwrap.json.
If dependencies in the package lock do not match those in package.json, npm ci will exit with an error, instead of updating the package lock.
npm ci can only install entire projects at a time: individual dependencies cannot be added with this command.
If a node_modules is already present, it will be automatically removed before npm ci begins its install.
It will never write to package.json or any of the package-locks: installs are essentially frozen.
The commands are very similar in functionality however the difference is in the approach taken to install the dependencies specified in your package.json and package-lock.json files.
npm ci performs a clean install of all the dependencies of your app whereas npm install may skip some installations if they already exist on the system. A problem may arise if the version already installed on the system isn't the one your package.json intended to install i.e. the installed version is different from the 'required' version.
Other differences would be that npm ci never touches your package*.json files. It will stop installation and show an error if the dependency versions do not match in the package.json and package-lock.json files.
You can read a much better explanation from the official docs here.
Additionally, you may want to read about package locks here.
It is worth having in mind that light node docker images like alpine do not have Python installed which is a dependency of node-gyp which is used by npm ci.
I think it's a bit opinionated that in order to have npm ci working you need to install Python as dependency in your build.
More info here Docker and npm - gyp ERR! not ok
npm ci - install exactly what is listed in package-lock.json
npm install - without changing any versions in package.json, use package.json to create/update package-lock.json, then install exactly what is listed in package-lock.json
npm update - update package.json packages to latest versions, then use package.json to create/update package-lock.json, then install exactly what is listed in package-lock.json
Or said a different way, npm ci changes 0 package files, npm install changes 1 package file, and npm update changes 2 package files.
It does a clean install, use it in situations where you would delete node_modules and re-run npm i.
I have no idea why some people think it's short for "continuous integration". There is an npm install command that can be run as npm i and an npm clean-install command that can be run as npm ci.
npm install is the command used to install the dependencies listed in a project's package.json file, while npm ci is a command that installs dependencies from a package-lock.json or npm-shrinkwrap.json file. The npm ci command is typically used in continuous integration (CI) environments, where the package-lock.json or npm-shrinkwrap.json file is checked into version control and should not be modified. Because npm ci installs dependencies from a locked file, it is a faster and more reliable way to install dependencies than npm install, which could install different versions of dependencies based on the state of the package.json file.
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.