How do you get `npm prune --production` to operate recursively? - node.js

Seems like a fairly straightforward thing, but I can't find anything on the interwebs.
It appears that npm prune --production only descends into the current package's node_modules folder. However, it does not recurse down the node_modules tree to remove devDependencies recursively. The result is that my project is a bit smaller in size thanks to the removal of its direct devDependencies, but the devDependencies of the project remain transitively, leaving it unnecessarily large in real projects.
Behavior is identical from npm versions 6.4.1 through 6.14.4.

Ok, figured it out, with not much help from the npm documentation.
To install only dependencies (that is, production dependencies only) recursively, you need two perform two steps:
Call npm ci. That installs only dependencies among a project's transitive dependencies, but it also installs a project's direct devDependencies.
Call npm prune --production. That gets rid of the top-level project's devDependencies.
With these steps, you can immensely reduce the size of your projects, which is helpful when they are deployables that create, say, Docker images. If you make sure to include RUN npm ci && npm prune --production in your Dockerfiles, your final image will be much smaller than if you don't, thanks to a much smaller node_modules directory.

Related

Do I also need to ignore the package-lock.json file when I ignore the node_modules folder from git? [duplicate]

npm 5 was released today and one of the new features include deterministic installs with the creation of a package-lock.json file.
Is this file supposed to be kept in source control?
I'm assuming it's similar to yarn.lock and composer.lock, both of which are supposed to be kept in source control.
Yes, package-lock.json is intended to be checked into source control. If you're using npm 5+, you may see this notice on the command line: created a lockfile as package-lock.json. You should commit this file. According to npm help 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.
One key detail about package-lock.json is that it cannot be published, and it
will be ignored if found in any place other than the toplevel package. It shares
a format with npm-shrinkwrap.json, which is essentially the same file, but
allows publication. This is not recommended unless deploying a CLI tool or
otherwise using the publication process for producing production packages.
If both package-lock.json and npm-shrinkwrap.json are present in the root of
a package, package-lock.json will be completely ignored.
Yes, you SHOULD:
commit the package-lock.json.
use npm ci instead of npm install when building your applications both on your CI and your local development machine
The npm ci workflow requires the existence of a package-lock.json.
A big downside of npm install command is its unexpected behavior that it may mutate the package-lock.json, whereas npm ci only uses the versions specified in the lockfile and produces an error
if the package-lock.json and package.json are out of sync
if a package-lock.json is missing.
Hence, running npm install locally, esp. in larger teams with multiple developers, may lead to lots of conflicts within the package-lock.json and developers to decide to completely delete the package-lock.json instead.
Yet there is a strong use-case for being able to trust that the project's dependencies resolve repeatably in a reliable way across different machines.
From a package-lock.json you get exactly that: a known-to-work state.
In the past, I had projects without package-lock.json / npm-shrinkwrap.json / yarn.lock files whose build would fail one day because a random dependency got a breaking update.
Those issue are hard to resolve as you sometimes have to guess what the last working version was.
If you want to add a new dependency, you still run npm install {dependency}. If you want to upgrade, use either npm update {dependency} or npm install ${dependendency}#{version} and commit the changed package-lock.json.
If an upgrade fails, you can revert to the last known working package-lock.json.
To quote npm doc:
It is highly recommended you commit the generated package lock to
source control: this will allow anyone else on your team, your
deployments, your CI/continuous integration, and anyone else who runs
npm install in your package source to get the exact same dependency
tree that you were developing on. Additionally, the diffs from these
changes are human-readable and will inform you of any changes npm has
made to your node_modules, so you can notice if any transitive
dependencies were updated, hoisted, etc.
And in regards to the difference between npm ci vs npm install:
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.
Note: I posted a similar answer here
Yes, it's intended to be checked in. I want to suggest that it gets its own unique commit. We find that it adds a lot of noise to our diffs.
Yes, the best practice is to check-in (YES, CHECK-IN)
I agree that it will cause a lot of noise or conflict when seeing the diff. But the benefits are:
guarantee exact same version of every package between your dev and prod environments. This part is the most important when building in different environments at different times. You may use ^1.2.3 in your package.json, but how can you ensure each time npm install will pick up the same version in your dev machine and in the build server, especially those indirect dependency packages? Well, package-lock.json will ensure that. (With the help of npm ci which installs packages based on lock file)
it improves the installation process.
it helps with new audit feature npm audit fix.
I don't commit this file in my projects. What's the point ?
It's generated
It's the cause of a SHA1 code integrity err in gitlab with gitlab-ci.yml builds
Though it's true that I never use ^ in my package.json for libs because I had bad experiences with it.
To the people complaining about the noise when doing git diff:
git diff -- . ':(exclude)*package-lock.json' -- . ':(exclude)*yarn.lock'
What I did was use an alias:
alias gd="git diff --ignore-all-space --ignore-space-at-eol --ignore-space-change --ignore-blank-lines -- . ':(exclude)*package-lock.json' -- . ':(exclude)*yarn.lock'"
To ignore package-lock.json in diffs for the entire repository (everyone using it), you can add this to .gitattributes:
package-lock.json binary
yarn.lock binary
This will result in diffs that show "Binary files a/package-lock.json and b/package-lock.json differ whenever the package lock file was changed. Additionally, some Git services (notably GitLab, but not GitHub) will also exclude these files (no more 10k lines changed!) from the diffs when viewing online when doing this.
Yes, you can commit this file. From the npm's official docs:
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[.]
Yes, it's a standard practice to commit package-lock.json.
The main reason for committing package-lock.json is that everyone in the project is on the same package version.
Pros:
If you follow strict versioning and don't allow updating to major versions automatically to save yourself from backward-incompatible changes in third-party packages committing package-lock helps a lot.
If you update a particular package, it gets updated in package-lock.json and everyone using the repository gets updated to that particular version when they take the pull of your changes.
Cons:
It can make your pull requests look ugly :)
npm install won't make sure that everyone in the project is on the same package version. npm ci will help with this.
Disable package-lock.json globally
type the following in your terminal:
npm config set package-lock false
this really work for me like magic
All answers say "YES" but that also depend of the project, the doc says:
One key detail about package-lock.json is that it cannot be published, and it will be ignored if found in any place other than the toplevel package.
This mean that you don't need to publish on npm your package-lock.json for dependency but you need to use package-lock.json in your repo to lock the version of your test dependency, build dependencies…
However, If your are using lerna for managing projects with multiple packages, you should put the package.json only on the root of your repo, not in each subpackage are created with npm init. You will get something like that :
.git
lerna.json
package.json
package-lock.json <--- here
packages/a/package.json
packages/a/lib/index.js
packages/b/package.json
packages/b/lib/index.js
My use of npm is to generate minified/uglified css/js and to generate the javascript needed in pages served by a django application. In my applications, Javascript runs on the page to create animations, some times perform ajax calls, work within a VUE framework and/or work with the css. If package-lock.json has some overriding control over what is in package.json, then it may be necessary that there is one version of this file. In my experience it either does not effect what is installed by npm install, or if it does, It has not to date adversely affected the applications I deploy to my knowledge. I don't use mongodb or other such applications that are traditionally thin client.
I remove package-lock.json from repo
because npm install generates this file, and npm install is part of the deploy process on each server that runs the app. Version control of node and npm are done manually on each server, but I am careful that they are the same.
When npm install is run on the server, it changes package-lock.json,
and if there are changes to a file that is recorded by the repo on the server, the next deploy WONT allow you to pull new changes from origin. That is
you can't deploy because the pull will overwrite the changes that have been made to package-lock.json.
You can't even overwrite a locally generated package-lock.json with what is on the repo (reset hard origin master), as npm will complain when ever you issue a command if the package-lock.json does not reflect what is in node_modules due to npm install, thus breaking the deploy. Now if this indicates that slightly different versions have been installed in node_modules, once again that has never caused me problems.
If node_modules is not on your repo (and it should not be), then package-lock.json should be ignored.
If I am missing something, please correct me in the comments, but the point that versioning is taken from this file makes no sense. The file package.json has version numbers in it, and I assume this file is the one used to build packages when npm install occurs, as when I remove it, npm install complains as follows:
jason#localhost:introcart_wagtail$ rm package.json
jason#localhost:introcart_wagtail$ npm install
npm WARN saveError ENOENT: no such file or directory, open '/home/jason/webapps/introcart_devtools/introcart_wagtail/package.json'
and the build fails, however when installing node_modules or applying npm to build js/css, no complaint is made if I remove package-lock.json
jason#localhost:introcart_wagtail$ rm package-lock.json
jason#localhost:introcart_wagtail$ npm run dev
> introcart#1.0.0 dev /home/jason/webapps/introcart_devtools/introcart_wagtail
> NODE_ENV=development webpack --progress --colors --watch --mode=development
10% building 0/1 modules 1 active ...
Committing package-lock.json to the source code version control means that the project will use a specific version of dependencies that may or may not match those defined in package.json. while the dependency has a specific version without any Caret (^) and Tilde (~) as you can see, that's mean the dependency will not be updated to the most recent version. and npm install will pick up the same version as well as we need it for our current version of Angular.
Note : package-lock.json highly recommended to commit it IF I added any Caret (^) and Tilde (~) to the dependency to be updated during the CI.

Npm remove test folders for production

npm prune --production removes packages from devDependencies. Is there any way to it also delete project folders that I do not want in production, such as my "spec" testing folder?
According to the docs, npm prune is used to mantain node_modules clean of extraneous packages, with the added functionality of removing dev dependencies when switching to production, so no, it doesn't seem configurable to perform such a task.
You could define a custom script in your package.json that will delete the folders and invoke npm prune after.

What does 'npm i --package-lock-only' do?

What does npm i --package-lock-only do exactly? The documentation is a tad shy on examples. https://docs.npmjs.com/cli/v6/configuring-npm/package-locks
I'm curious to know if I have older packages in my local node_modules folder and no package-lock.json file, will npm i --package-lock-only generate a package-lock.json according to the version in my local node_modules folder or will it generate a package-lock.json with newer package versions that is consistent with the semver ranges in the package.json that's published in the npm registry.
It will determine versions of packages to install using package.json, and then create a package-lock.json file with its resolved versions if none exists, or overwrite an existing one.
Significantly, it does not actually install anything, which is what distinguishes it from regular npm install (or the aliased npm i).
Well, #Ben Wheeler is acurate, but there's a place to give a little background on this process. In regular situation the package-lock is meant for set a complete dependency tree of every package and it's dependencies in your application, so every developer on a different machine will have the exact same tree. This is important because the dependencies packages might be updated with time and if every developer will use different versions it could break your application. So every time you do "npm i" if you do have a package.lock.json it actually install the packages from there and not from package.json.
Sometimes when developers have a dependencies errors they tend to delete the lock file and the node_modules. which is not always the best option. Most of the time it's enough to update only the lock file to reflect the package.json with the flag --package-lock-only, and then you can do again "npm i" to install your packages. The lock file should be committed to your project repo so everyone can use it to have the same packages version.
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.
Facilitate greater visibility of tree changes through readable source
control diffs.
Optimize the installation process by allowing npm to skip repeated
metadata resolutions for previously-installed packages.
As of npm v7, lockfiles include enough information to gain a complete
picture of the package tree, reducing the need to read package.json
files, and allowing for significant performance improvements.

Do I commit the package-lock.json file created by npm 5?

npm 5 was released today and one of the new features include deterministic installs with the creation of a package-lock.json file.
Is this file supposed to be kept in source control?
I'm assuming it's similar to yarn.lock and composer.lock, both of which are supposed to be kept in source control.
Yes, package-lock.json is intended to be checked into source control. If you're using npm 5+, you may see this notice on the command line: created a lockfile as package-lock.json. You should commit this file. According to npm help 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.
One key detail about package-lock.json is that it cannot be published, and it
will be ignored if found in any place other than the toplevel package. It shares
a format with npm-shrinkwrap.json, which is essentially the same file, but
allows publication. This is not recommended unless deploying a CLI tool or
otherwise using the publication process for producing production packages.
If both package-lock.json and npm-shrinkwrap.json are present in the root of
a package, package-lock.json will be completely ignored.
Yes, you SHOULD:
commit the package-lock.json.
use npm ci instead of npm install when building your applications both on your CI and your local development machine
The npm ci workflow requires the existence of a package-lock.json.
A big downside of npm install command is its unexpected behavior that it may mutate the package-lock.json, whereas npm ci only uses the versions specified in the lockfile and produces an error
if the package-lock.json and package.json are out of sync
if a package-lock.json is missing.
Hence, running npm install locally, esp. in larger teams with multiple developers, may lead to lots of conflicts within the package-lock.json and developers to decide to completely delete the package-lock.json instead.
Yet there is a strong use-case for being able to trust that the project's dependencies resolve repeatably in a reliable way across different machines.
From a package-lock.json you get exactly that: a known-to-work state.
In the past, I had projects without package-lock.json / npm-shrinkwrap.json / yarn.lock files whose build would fail one day because a random dependency got a breaking update.
Those issue are hard to resolve as you sometimes have to guess what the last working version was.
If you want to add a new dependency, you still run npm install {dependency}. If you want to upgrade, use either npm update {dependency} or npm install ${dependendency}#{version} and commit the changed package-lock.json.
If an upgrade fails, you can revert to the last known working package-lock.json.
To quote npm doc:
It is highly recommended you commit the generated package lock to
source control: this will allow anyone else on your team, your
deployments, your CI/continuous integration, and anyone else who runs
npm install in your package source to get the exact same dependency
tree that you were developing on. Additionally, the diffs from these
changes are human-readable and will inform you of any changes npm has
made to your node_modules, so you can notice if any transitive
dependencies were updated, hoisted, etc.
And in regards to the difference between npm ci vs npm install:
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.
Note: I posted a similar answer here
Yes, it's intended to be checked in. I want to suggest that it gets its own unique commit. We find that it adds a lot of noise to our diffs.
Yes, the best practice is to check-in (YES, CHECK-IN)
I agree that it will cause a lot of noise or conflict when seeing the diff. But the benefits are:
guarantee exact same version of every package between your dev and prod environments. This part is the most important when building in different environments at different times. You may use ^1.2.3 in your package.json, but how can you ensure each time npm install will pick up the same version in your dev machine and in the build server, especially those indirect dependency packages? Well, package-lock.json will ensure that. (With the help of npm ci which installs packages based on lock file)
it improves the installation process.
it helps with new audit feature npm audit fix.
I don't commit this file in my projects. What's the point ?
It's generated
It's the cause of a SHA1 code integrity err in gitlab with gitlab-ci.yml builds
Though it's true that I never use ^ in my package.json for libs because I had bad experiences with it.
To the people complaining about the noise when doing git diff:
git diff -- . ':(exclude)*package-lock.json' -- . ':(exclude)*yarn.lock'
What I did was use an alias:
alias gd="git diff --ignore-all-space --ignore-space-at-eol --ignore-space-change --ignore-blank-lines -- . ':(exclude)*package-lock.json' -- . ':(exclude)*yarn.lock'"
To ignore package-lock.json in diffs for the entire repository (everyone using it), you can add this to .gitattributes:
package-lock.json binary
yarn.lock binary
This will result in diffs that show "Binary files a/package-lock.json and b/package-lock.json differ whenever the package lock file was changed. Additionally, some Git services (notably GitLab, but not GitHub) will also exclude these files (no more 10k lines changed!) from the diffs when viewing online when doing this.
Yes, you can commit this file. From the npm's official docs:
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[.]
Yes, it's a standard practice to commit package-lock.json.
The main reason for committing package-lock.json is that everyone in the project is on the same package version.
Pros:
If you follow strict versioning and don't allow updating to major versions automatically to save yourself from backward-incompatible changes in third-party packages committing package-lock helps a lot.
If you update a particular package, it gets updated in package-lock.json and everyone using the repository gets updated to that particular version when they take the pull of your changes.
Cons:
It can make your pull requests look ugly :)
npm install won't make sure that everyone in the project is on the same package version. npm ci will help with this.
Disable package-lock.json globally
type the following in your terminal:
npm config set package-lock false
this really work for me like magic
All answers say "YES" but that also depend of the project, the doc says:
One key detail about package-lock.json is that it cannot be published, and it will be ignored if found in any place other than the toplevel package.
This mean that you don't need to publish on npm your package-lock.json for dependency but you need to use package-lock.json in your repo to lock the version of your test dependency, build dependencies…
However, If your are using lerna for managing projects with multiple packages, you should put the package.json only on the root of your repo, not in each subpackage are created with npm init. You will get something like that :
.git
lerna.json
package.json
package-lock.json <--- here
packages/a/package.json
packages/a/lib/index.js
packages/b/package.json
packages/b/lib/index.js
My use of npm is to generate minified/uglified css/js and to generate the javascript needed in pages served by a django application. In my applications, Javascript runs on the page to create animations, some times perform ajax calls, work within a VUE framework and/or work with the css. If package-lock.json has some overriding control over what is in package.json, then it may be necessary that there is one version of this file. In my experience it either does not effect what is installed by npm install, or if it does, It has not to date adversely affected the applications I deploy to my knowledge. I don't use mongodb or other such applications that are traditionally thin client.
I remove package-lock.json from repo
because npm install generates this file, and npm install is part of the deploy process on each server that runs the app. Version control of node and npm are done manually on each server, but I am careful that they are the same.
When npm install is run on the server, it changes package-lock.json,
and if there are changes to a file that is recorded by the repo on the server, the next deploy WONT allow you to pull new changes from origin. That is
you can't deploy because the pull will overwrite the changes that have been made to package-lock.json.
You can't even overwrite a locally generated package-lock.json with what is on the repo (reset hard origin master), as npm will complain when ever you issue a command if the package-lock.json does not reflect what is in node_modules due to npm install, thus breaking the deploy. Now if this indicates that slightly different versions have been installed in node_modules, once again that has never caused me problems.
If node_modules is not on your repo (and it should not be), then package-lock.json should be ignored.
If I am missing something, please correct me in the comments, but the point that versioning is taken from this file makes no sense. The file package.json has version numbers in it, and I assume this file is the one used to build packages when npm install occurs, as when I remove it, npm install complains as follows:
jason#localhost:introcart_wagtail$ rm package.json
jason#localhost:introcart_wagtail$ npm install
npm WARN saveError ENOENT: no such file or directory, open '/home/jason/webapps/introcart_devtools/introcart_wagtail/package.json'
and the build fails, however when installing node_modules or applying npm to build js/css, no complaint is made if I remove package-lock.json
jason#localhost:introcart_wagtail$ rm package-lock.json
jason#localhost:introcart_wagtail$ npm run dev
> introcart#1.0.0 dev /home/jason/webapps/introcart_devtools/introcart_wagtail
> NODE_ENV=development webpack --progress --colors --watch --mode=development
10% building 0/1 modules 1 active ...
Committing package-lock.json to the source code version control means that the project will use a specific version of dependencies that may or may not match those defined in package.json. while the dependency has a specific version without any Caret (^) and Tilde (~) as you can see, that's mean the dependency will not be updated to the most recent version. and npm install will pick up the same version as well as we need it for our current version of Angular.
Note : package-lock.json highly recommended to commit it IF I added any Caret (^) and Tilde (~) to the dependency to be updated during the CI.

How do I check node_modules directory for unnecessary packages?

My node_modules has packages that are not listed in my package.json's dependencies, so I'm guessing that those packages are dependencies of my dependencies. How would I be able to check this? I want to make sure that there aren't any unnecessary packages in my node_modules directory.
If your dependency list won't take too long to reinstall, a simple option is a table-flip: remove the node_modules directory entirely and run npm install to re-create it.
If you don't want to do that, you can try tools that inspect your dependencies, like depcheck as #sagar-gopale suggests in their answer.
Related: Run npm -v to find out if you are running npm v2 or v3. Like #cartant says in their answer, with v3, your node_modules directory will be maximally flat, which means things that used to appear as subdirectories of other modules (when installed with npm v2) will now appear at the top level of node_modules itself. That may be the reason you see more modules than you expect.
If you are using NPM 3, you will likely see a large number of modules that you were not expecting to see in the node_modules directory, as NPM 3 flattens the dependency hierarchy.
Whichever version you are using, if you run the npm list command, NPM should highlight any extraneous modules that are not required.
Please checkout this package.
https://www.npmjs.com/package/depcheck
Since packages can require other packages, just because there are packages in the node_modules folder that don't exist in your packages.json file doesn't mean they aren't needed by one of your specified packages.
If you run an npm prune command on the root directory of your solution it will read the dependency tree and remove the packages that are truly no longer needed.

Resources