Should an npm eslint-config package place the config package it extends within devDependencies? - dependency-management

I am publishing a sharable eslint-config on npm. My configuration extends eslint-config-airbnb. Should I install eslint-config-airbnb as a devDependency?
When I npm i --save-dev eslint-config-myconfig in another project will the eslint-config-myconfig dependencies be bundled in production builds?
//index.js
module.exports = {
"extends": "airbnb",
"rules": {... my overrides}
}
//package.json
...
"peerDependencies": {
"eslint": ">= 4"
},
"dependencies": {
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.2",
"eslint-plugin-react": "^7.11.1"
}
https://eslint.org/docs/developer-guide/shareable-configs#creating-a-shareable-config

I was just wondering the same thing and the link you provided actually explains all that:
If your shareable config depends on a plugin, you should also specify it as a peerDependency (plugins will be loaded relative to the end user’s project, so the end user is required to install the plugins they need). However, if your shareable config depends on a third-party parser or another shareable config, you can specify these packages as dependencies.
So
eslint itself and plugins → peerDependency
parsers and configs → dependency
In your case the three plugins must be moved into the peer dependencies.

Related

Local path dependencies not installing their own dependencies

Following this answer, I installed my local dependency like this:
{
"private": true,
"dependencies": {
"my_dependency": "../relative/path/to/my_dependency"
}
}
my_dependency depends on ESLint and its plugins:
{
"name": "my_dependency",
"dependencies": {
"#typescript-eslint/eslint-plugin": "5.25.0",
"#typescript-eslint/parser": "5.25.0",
"eslint": "8.16.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-node": "11.1.0"
},
If I install my_dependency as a path, eslint and it's plugins are not installed in ./node_modules, therefore eslint CLI is not available.
However, if I publish my_dependency on npm and install it as package name and version,
{
"private": true,
"dependencies": {
"my_dependency": "0.0.0"
}
}
eslint and all plugins will be added in ./node_modules.
How to get the same effect for the local dependency?
I don't want to pollute npm registry with versions just for experiment each time I make changes in my_dependency.
The issue
Unfortunately those local path packages don't get their dependencies installed.
note: Packages linked by local path will not have their own dependencies installed when npm install is ran in this case. You must run npm install from inside the local path itself.
Source: docs.npmjs.com/...
The workaround
There are quite a few ways around it but in my opinion the best one is to use a GitHub repo and optionally a branch.
So just push your code to a github repo, let's say my-npm-playground, assuming your use your Github username is ada_lovelace, you can link the repo like so,
{
"private": true,
"dependencies": {
"my_dependency": "ada_lovelace/my-npm-playground"
}
}

The dependency (non-development) of other dependency has not been automatically added to node_modules

I has been told
ESLint couldn't find the plugin "#typescript-eslint/eslint-plugin".
(The package "#typescript-eslint/eslint-plugin" was not found when loaded as a Node module from the directory "D:\XXX\NNN".)
It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:
npm install #typescript-eslint/eslint-plugin#latest --save-dev
by ESLint. Why it's strange is the package with ESLint preset has #typescript-eslint/eslint-plugin among dependencies:
{
"name": "#yamato_daiwa/style_guides",
"dependencies": {
"#typescript-eslint/eslint-plugin": "5.19.0",
"#typescript-eslint/parser": "5.19.0",
"eslint": "8.13.0",
"eslint-plugin-node": "11.1.0"
},
"peerDependencies": {
"#typescript-eslint/eslint-plugin": "^5.18.0",
"#typescript-eslint/parser": "^5.18.0",
"eslint": "^8.13.0",
"eslint-plugin-node": "^11.1.0",
"typescript": ">=4.0.0 <4.7.0"
}
}
(I am not sure that peerDependencies are required but I must keep this question focues on one problem)
I expected that all dependencies will be automatically added to node_modules but it has not been (there is no #typescript-eslint among scope directories):
My node version is 16.3.
Update
Looks like #typescript-eslint/eslint-plugin has been added to node_modules below library with ESLint preset. I expected it will be added to directory below node_modules of the project, not below project/node_modules/#yamato-daiwa/style_guides/node_modules.
Do you really need eslint-plugin as a peerDependency in your project?
If so, inspect that you're trying to install the same version of eslint-plugin as the lib that is requiring it.
If not, remove it.
according to documentation: "When a dependency is listed in a package as a peerDependency, it is not automatically installed. Instead, the code that includes the package must include it as its dependency.
npm will warn you if you run npm install and it does not find this dependency."
https://flaviocopes.com/npm-peer-dependencies/
If all else fails, try running "npm ci" (clean install) rather than npm install.

npm not installing dependency of dev dependency when it's also listed as app dependency

This one has me stumped. Very easily reproducible.
package.json
{
"dependencies": {},
"devDependencies": {
"gulp": "^3.9.1",
"gulp-less": "^4.0.1"
}
}
Put that in a folder and run npm install --only=dev. I'm using node v8.9.4 and npm v6.4.1.
Everything will install fine. Open the node_modules directory and verify that the less module is there, because it's a dependency of gulp-less. You can use this gulpfile.js to test:
var gulp = require('gulp');
var less = require('gulp-less');
gulp.task('default')
Okay, now add "less": "^3.8.1" to "dependencies" so your package.json looks like this:
{
"dependencies": {
"less": "^3.8.1"
},
"devDependencies": {
"gulp": "^3.9.1",
"gulp-less": "^4.0.1"
}
}
Remove the node_modules folder and package lock file and re-run npm install --only=dev.
You will see that you cannot run the gulp file now, and if you open and look for the less package in node_modules it will not have been installed.
This is repeatable behavior for me. Any time I add less to the app dependencies, it is never installed when I do install --only=dev.
Am I missing something about how this is supposed to work or did I find a bug?

How to I prevent module dependencies from always grabbing the latest version when running npm install? [duplicate]

I would like to use the grunt-contrib-jasmine NPM package. It has various dependencies. Part of the dependency graph looks like this:
─┬ grunt-contrib-jasmine#0.4.1
│ ├─┬ grunt-lib-phantomjs#0.2.0
│ │ ├─┬ phantomjs#1.8.2-2
Unfortunately, there's a bug in this version phantomjs which prevents it from installing correctly on Mac OS X. This is fixed in the latest version.
How can I get grunt-lib-phantomjs to use a newer version of phantomjs?
Some additional context:
grunt-contrib-jasmine explicitly requires version "~0.2.0" of grunt-lib-phantomjs, which explicitly requires version "~1.8.1" of phantomjs.
Adding phantomjs to my package's dependencies first has no effect; both versions are installed and grunt-contrib-jasmine still uses the older versions (see: When installing a package with NPM, can you tell it to use a different version of one of its dependencies?).
You can use npm shrinkwrap functionality, in order to override any dependency or sub-dependency.
I've just done this in a grunt project of ours. We needed a newer version of connect, since 2.7.3. was causing trouble for us. So I created a file named npm-shrinkwrap.json:
{
"dependencies": {
"grunt-contrib-connect": {
"version": "0.3.0",
"from": "grunt-contrib-connect#0.3.0",
"dependencies": {
"connect": {
"version": "2.8.1",
"from": "connect#~2.7.3"
}
}
}
}
}
npm should automatically pick it up while doing the install for the project.
(See: https://nodejs.org/en/blog/npm/managing-node-js-dependencies-with-shrinkwrap/)
As of npm cli v8.3.0 (2021-12-09) this can be solved using the overrides field of package.json. As described in StriplingWarrior's answer
For example, the project has typescript version 4.6.2 as direct development dependency and awesome-typescript-loader that uses old version 2.7 of typescript. Here is how you can tell npm to use version 4.6.2 of typescript for awesome-typescript-loader:
{
"name": "myproject",
"version": "0.0.0",
"scripts": ...
"dependencies": ...
"devDependencies": {
"typescript": "~4.6.2",
"awesome-typescript-loader": "^5.2.1",
...
},
"overrides": {
"awesome-typescript-loader": {
"typescript": "$typescript"
}
}
}
If you don't use typescript as direct development dependency, then you have to write 4.6.2 instead of $typescript in overrides section:
{
"name": "myproject",
"version": "0.0.0",
"scripts": ...
"dependencies": ...
"devDependencies": {
"awesome-typescript-loader": "^5.2.1",
...
},
"overrides": {
"awesome-typescript-loader": {
"typescript": "~4.6.2"
}
}
}
For using the latest version of dependency:
{
"name": "myproject",
"version": "0.0.0",
"scripts": ...
"dependencies": ...
"devDependencies": {
"awesome-typescript-loader": "^5.2.1",
...
},
"overrides": {
"awesome-typescript-loader": {
"typescript": "latest"
}
}
}
Same overrides can be used for both dependencies and devDependencies.
If you're using npm version >5 but <8.3.0: edit your package-lock.json: remove the library from "requires" section and add it under "dependencies".
For example, you want deglob package to use glob package version 3.2.11 instead of its current one. You open package-lock.json and see:
"deglob": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.0.tgz",
"integrity": "sha1-TUSr4W7zLHebSXK9FBqAMlApoUo=",
"requires": {
"find-root": "1.1.0",
"glob": "7.1.2",
"ignore": "3.3.5",
"pkg-config": "1.1.1",
"run-parallel": "1.1.6",
"uniq": "1.0.1"
}
},
Remove "glob": "7.1.2", from "requires", add "dependencies" with proper version:
"deglob": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.0.tgz",
"integrity": "sha1-TUSr4W7zLHebSXK9FBqAMlApoUo=",
"requires": {
"find-root": "1.1.0",
"ignore": "3.3.5",
"pkg-config": "1.1.1",
"run-parallel": "1.1.6",
"uniq": "1.0.1"
},
"dependencies": {
"glob": {
"version": "3.2.11"
}
}
},
Now remove your node_modules folder, run npm ci (or npm install for old version of node/npm) and it will add missing parts to the "dependencies" section.
As of NPM v8.3, the correct way to deal with this is via the overrides section of your package.json file.
If you need to make specific changes to dependencies of your
dependencies, for example replacing the version of a dependency with a
known security issue, replacing an existing dependency with a fork, or
making sure that the same version of a package is used everywhere,
then you may add an override.
Overrides provide a way to replace a package in your dependency tree
with another version, or another package entirely. These changes can
be scoped as specific or as vague as desired.
To make sure the package foo is always installed as version 1.0.0 no
matter what version your dependencies rely on:
{
"overrides": {
"foo": "1.0.0"
}
}
There are a variety of other, more nuanced configurations allowing you to only override a package when it's a dependency of a particular package hierarchy. For more details, check out https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides
The only solution that worked for me (node 12.x, npm 6.x) was using npm-force-resolutions developed by #Rogerio Chaves.
First, install it by:
npm install npm-force-resolutions --save-dev
You can add --ignore-scripts if some broken transitive dependency scripts are blocking you from installing anything.
Then in package.json define what dependency should be overridden (you must set exact version number):
"resolutions": {
"your-dependency-name": "1.23.4"
}
and in "scripts" section add new preinstall entry:
"preinstall": "npm-force-resolutions",
Now, npm install will apply changes and force your-dependency-name to be at version 1.23.4 for all dependencies.
For those using yarn.
I tried using npm shrinkwrap until I discovered the yarn cli ignored my npm-shrinkwrap.json file.
Yarn has https://yarnpkg.com/lang/en/docs/selective-version-resolutions/ for this. Neat.
Check out this answer too: https://stackoverflow.com/a/41082766/3051080
Nested replacement with an entirely different package
Most of the strategies outlined in the other answers here work well if you are just interested in overriding the package's version number, but in our case, we needed to find a way to override a nested npm sub-dependency with a different package altogether. For details on why you would ever want to do this, please refer to the following question:
How to override a nested npm sub-dependency with a different package altogether (not just different package version number)?
Specify the tarball directly
For nested replacement of a package with an entirely different package using the npm-force-resolutions strategy that others have mentioned, you just need to provide a link to the tarball where you would normally specify the overriding version number.
As an example, for the case of replacing the vulnerable package, ansi-html, with the fixed fork of this package, ansi-html-community, your resolutions section of package.json should look like this:
"resolutions": {
"ansi-html": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz"
}
To find the link to the tarball, use the following command, modifying your registry as necessary:
npm view ansi-html-community dist.tarball --registry=https://registry.npmjs.org/
Also, note that for npm-force-resolutions to work when you run npm install, you will need a preinstall entry under the scripts section of package.json:
"scripts": {
"preinstall": "npx npm-force-resolutions"
}
#user11153 's answer worked for me locally, but when trying to do a clean install (aka deleting node_modules), I would get:
npm-force-resolutions: command not found
I had to update the preinstall script to be:
"preinstall": "npm i npm-force-resolutions && npm-force-resolutions"
Which ensures that npm-force-resolutions package is installed before attempting to run it.
That being said, if you're able to use yarn instead, I would do that and then use #Gus 's answer.
I had an issue where one of the nested dependency had an npm audit vulnerability, but I still wanted to maintain the parent dependency version. the npm shrinkwrap solution didn't work for me, so what I did to override the nested dependency version:
Remove the nested dependency under the 'requires' section in package-lock.json
Add the updated dependency under DevDependencies in package.json, so that modules that require it will still be able to access it.
npm i
I was about to go down the npm-force-resolutions route but it seems that simply including the dependency in my own package.json fixed the problem for me.
I believe this worked in my case because the original dependency allows for patch versions of the dependency in question that I wanted to update. Thus by manually including a newer version it still fulfilled the dependency of the original dependency and will use the one I've manually added.
Example
Problem
I need to update plyr to version 3.6.9 from 3.6.8
Mine
package.json
{
"dependencies": {
"react-plyr": "^3.2.0"
}
}
React Plyr
package.json
{
"dependencies": {
"plyr": "^3.6.8"
}
}
Notice for the plyr dependency it starts with ^ this means it can accept any minor patches. You can learn more about that here:
https://docs.npmjs.com/about-semantic-versioning#using-semantic-versioning-to-specify-update-types-your-package-can-accept
Updating Mine
This updates the plyr dependency from my package.json.
package.json
{
"dependencies": {
"plyr": "^3.6.9",
"react-plyr": "^3.2.0"
}
}
Based on the rest of the answers, I provide the same solution, but I display the package.json, as I struggled a little bit on where to place the override and how.
{
"name": "my-app",
"version": "snapshot",
"scripts": {
"ng": "ng",
"build-dev": "ng build --configuration development",
},
"private": true,
"dependencies": {
"#angular/animations": "~14.2.9",
"#angular/common": "~14.2.9"
...
},
"devDependencies": {
"#angular-devkit/build-angular": "^14.2.8",
....
},
"overrides": {
"loader-utils#>2.0.0 <3": "2.0.4",
"loader-utils#>3.0.0 <4": "3.2.1"
}
}
For November 2022 "loader-utils" security vulnerability, it was requested to
use the version 2.0.4, if you are in the 2.X
use the version 3.2.1, if you are in the 3.X
And to verify
add the package.json the override tag
delete the package-lock.json
run "npm install"
run "npm audit"
Run this first
npm i -D #types/eslint#8.4.3
it will solve the issue

Private NPM Repository and hardcoded github URLs

NPM packages allow dependencies to hardcode a URL where the dependency is found.
For example intern depends on dojo:
"dependencies": {
"istanbul": "0.2.16",
"source-map": "0.1.33",
"dojo": "https://github.com/csnover/dojo2-core/archive/ebfa11ba3972944218623a4bd9d124cb8108d70c.tar.gz",
"chai": "1.9.1",
"leadfoot": "1.2.1",
"digdug": "1.2.1",
"charm": "0.2.0",
"diff": "1.1.0"
},
We are using a private NPM repository (Nexus Repository Manager) but when we publish a package to it these "non-node package" dependencies are not included and so "npm install" fails. Fetching dependencies from "the cloud" is not an option for us.
Has anyone else come up against this problem and solved it in a scalable way? I'd rather not manually rewrite the package.json for every NPM package that does this to point to some internal URL.

Resources