我应该在“package.json”的“dependencies”字段中复制“peerDependencies”吗?
为了实验,我下载了@typescript-eslint/eslint-plugin的源代码。这个包有两个对等依赖项:
{
"peerDependencies": {
"@typescript-eslint/parser": "^4.0.0",
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
},
"dependencies": {
"@typescript-eslint/experimental-utils": "4.11.1",
"@typescript-eslint/scope-manager": "4.11.1",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0",
"semver": "^7.3.2",
"tsutils": "^3.17.1"
},
}
{
"peerDependencies": {
"@typescript-eslint/parser": "^4.0.0",
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
},
"dependencies": {
"@typescript-eslint/experimental-utils": "4.11.1",
"@typescript-eslint/scope-manager": "4.11.1",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0",
"semver": "^7.3.2",
"tsutils": "^3.17.1"
},
}
如果我npm list在安装所有依赖项后运行,我会得到:
npm ERR! peer dep missing: eslint@^5.0.0 || ^6.0.0 || ^7.0.0, required by @typescript-eslint/eslint-plugin@4.11.1
npm ERR! peer dep missing: eslint@*, required by @typescript-eslint/experimental-utils@4.11.1
npm ERR! peer dep missing: eslint@^5.0.0 || ^6.0.0 || ^7.0.0, required by @typescript-eslint/eslint-plugin@4.11.1
npm ERR! peer dep missing: eslint@*, required by @typescript-eslint/experimental-utils@4.11.1
这是否意味着npm想要:
回答
@Daniel_Knights 主要回答了这个问题。但我也想加上我的两分钱。所以这里是:
NPM 中的依赖类型:
为了理解这一点,了解 NPM 包中不同类型的依赖项很重要。一般来说,NPM 中有 4 种依赖类型:
-
直接依赖(或简单的依赖):这些依赖是 NPM 包运行所绝对必需的。如果您正在使用 express.js 构建 Web 应用程序,那么您绝对希望
express安装 以便您的应用程序启动。因此,这将是您的应用程序的直接依赖项。这些应列在"dependencies": {}部分下package.json。 -
开发依赖:这些依赖在开发应用程序时很有帮助,但不一定被应用程序包用来运行。这种依赖的一个例子是
typescript. NodeJS 不理解 Typescript。因此,即使您可以在 Typescript 中编写应用程序,在通过 typescript 编译器运行它之后,您也只能使用 Javascript。因此,即使您需要typescript在开发过程中添加包,您的应用程序在编译后也不需要它来运行。
因此,如果您添加typescript到您的"devDependencies": {}部分package.json并执行npm install,NPM 将安装依赖项和 devDependencies。在此阶段,您可以调用 Typescript 编译器来构建应用程序。但是在那之后,您可以运行npm prune --production,NPM 将从node_modules/. 这减少了最终应用程序包的大小并使其不受任何开发依赖项的影响。
你不应该在你的源代码中引用任何开发依赖,除非你的代码安全和优雅地回退到替代品,因为包将在修剪时被删除。
- 可选的依赖:这些都是你可以在里面指定依赖
"optionalDependencies": {}的部分package.json。当您将依赖项指定为可选时,您会让 NPM 知道“如果该依赖项可用,您的程序将使用该依赖项。如果不可用,那也很酷。它将使用其他东西。”
这可以帮助的一个常见场景是使用数据库驱动程序。用 JS 编写的数据库驱动程序不是特别有效或高性能。因此,通常使用具有本机绑定的驱动程序(使用本机 (C/C++) 包运行其任务的 JS 库)。但问题是对于本地绑定,本地包必须安装在运行应用程序的机器上。这可能并不总是可用。所以我们可以指定一个本地库作为可选库。您可以在 JS 代码中引用它,例如:
var pg = require('pg-native'); // Native binding library
if (!pg) { // If it's not available...
pg = require('pg'); // ...use non native library.
}
因此,在使用 安装软件包时npm install,NPM 也会尝试安装一个可选的依赖项。但是如果它无法安装(可能是因为本机绑定不可用),它就不会出错。它只会发布警告并继续前进。
现在是有问题的依赖类型......
- 同行的依赖:正如你已经知道,这是你的内部指定依赖
"peerDependencies": {}的部分package.json。与上述其他三个依赖项不同,NPM在执行npm install. 这是因为 NPM 期望这些依赖项由其他依赖项提供。
我们将看到为什么这是有道理的,但我们必须绕道而行,以了解 NPM 如何在node_modules/文件夹中构建依赖关系。
NPM 如何存储依赖
让我们用一个例子来做这个:
我们将初始化一个 npm 包并express作为依赖项安装:
$ npm install express --save
如果我们现在查看node_modules/目录,我们可以看到它qs安装了软件包express:
$ ls -l node_modules/
total 196
// ...more stuff...
drwxr-xr-x 3 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 express <---------- here is our express
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 finalhandler
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 forwarded
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 fresh
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 http-errors
drwxr-xr-x 4 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 iconv-lite
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 inherits
drwxr-xr-x 3 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 ipaddr.js
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 media-typer
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 merge-descriptors
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 methods
drwxr-xr-x 3 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 mime
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 mime-db
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 mime-types
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 ms
drwxr-xr-x 3 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 negotiator
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 on-finished
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 parseurl
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 path-to-regexp
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 proxy-addr
drwxr-xr-x 5 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 qs <---------- focus here for a bit
drwxr-xr-x 2 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 range-parser
// ...even more stuff ...
现在,node_modules/文件express/夹中没有文件夹,即使它有一个package.json:
$ ls -l node_modules/express/
total 132
-rw-r--r-- 1 rajshrimohanks rajshrimohanks 109589 Oct 26 1985 History.md
-rw-r--r-- 1 rajshrimohanks rajshrimohanks 1249 Oct 26 1985 LICENSE
-rw-r--r-- 1 rajshrimohanks rajshrimohanks 4607 Oct 26 1985 Readme.md
-rw-r--r-- 1 rajshrimohanks rajshrimohanks 224 Oct 26 1985 index.js
drwxr-xr-x 4 rajshrimohanks rajshrimohanks 4096 Dec 31 16:00 lib
-rw-r--r-- 1 rajshrimohanks rajshrimohanks 3979 Dec 31 16:00 package.json
如果你看package.json的的express包,你会看到,它需要qs包版本6.7.0:
$ cat node_modules/express/package.json
{
// other stuff ...
"dependencies": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0", <-------------- this is what we are looking at
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
// ... more stuff ...
}
所以express需要qs版本,6.7.0所以 NPM 把它放在一起express使用。
$ cat node_modules/qs/package.json
{
// ... stuff ...
"name": "qs",
"repository": {
"type": "git",
"url": "git+https://github.com/ljharb/qs.git"
},
"scripts": {
"coverage": "covert test",
"dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js",
"lint": "eslint lib/*.js test/*.js",
"postlint": "editorconfig-tools check * lib/* test/*",
"prepublish": "safe-publish-latest && npm run dist",
"pretest": "npm run --silent readme && npm run --silent lint",
"readme": "evalmd README.md",
"test": "npm run --silent coverage",
"tests-only": "node test"
},
"version": "6.7.0" <---- this version
}
现在让我们看看如果我们想qs在我们的应用程序中使用BUT 版本会发生什么6.8.0。
$ npm install qs@6.8.0 --save
npm WARN dep-test@1.0.0 No description
npm WARN dep-test@1.0.0 No repository field.
+ qs@6.8.0
added 2 packages from 1 contributor, updated 1 package and audited 52 packages in 0.796s
found 0 vulnerabilities
$ cat node_modules/qs/package.json
{
//... other stuff ...
"name": "qs",
"repository": {
"type": "git",
"url": "git+https://github.com/ljharb/qs.git"
},
"scripts": {
"coverage": "covert test",
"dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js",
"lint": "eslint lib/*.js test/*.js",
"postlint": "eclint check * lib/* test/*",
"prepublish": "safe-publish-latest && npm run dist",
"pretest": "npm run --silent readme && npm run --silent lint",
"readme": "evalmd README.md",
"test": "npm run --silent coverage",
"tests-only": "node test"
},
"version": "6.8.0" <-------- the version changed!
}
NPM 替换了6.8.0我们想要的版本。但是对于需要at的express包的需求呢?不用担心,NPM 通过提供自己的at本地副本来处理它。qs6.7.0expressqs6.7.0
$ cat node_modules/express/node_modules/qs/package.json
{
// ... other stuff ...
"name": "qs",
"repository": {
"type": "git",
"url": "git+https://github.com/ljharb/qs.git"
},
"scripts": {
"coverage": "covert test",
"dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js",
"lint": "eslint lib/*.js test/*.js",
"postlint": "editorconfig-tools check * lib/* test/*",
"prepublish": "safe-publish-latest && npm run dist",
"pretest": "npm run --silent readme && npm run --silent lint",
"readme": "evalmd README.md",
"test": "npm run --silent coverage",
"tests-only": "node test"
},
"version": "6.7.0" <----- just what express wants!
}
所以,你可以看到,NPM增加了当地node_modules的express孤独,给了它自己的版本。这就是 NPM 确保我们的应用程序以及express满足他们自己的要求的方式。但这里有一个关键要点:
“如果多个包需要另一个相同但版本不同的包,NPM 将安装多个副本,为每个包安装多个副本以满足它们。”
在某些情况下,这可能并不总是理想的。假设我们的包想要使用,qs但我们不关心它是什么版本,只要它高于版本,6.0.0并且我们确信其他一些包,例如express也将一起使用(它有自己的qsat 6.7.0)。在这种情况下,我们可能不希望 NPM 安装另一个增加批量的副本。相反,我们可以指定qs为...对等依赖!
现在 NPM 不会自动安装对等依赖项。但是会期望它由其他一些包提供。
所以最后,来到你的案子......
在以下情况下@typescript-eslint/eslint-plugin:
@typescript-eslint/eslint-plugin旨在与@typescript-eslint/parser和eslint包一起使用。如果@typescript-eslint/eslint-plugin不使用它们,您就无法使用,因为所有这些都是一个更大的包的一部分eslint,可以帮助您整理 Typescript 和 JS 代码。所以你eslint无论如何都会安装,这将是使用@typescript-eslint/eslint-plugin.
因此,作者认为添加它们是合适的,@typescript-eslint/eslint-plugin只要您eslint在5.x.x、6.x.x或7.x.x系列中有任何次要版本,就不必在意。@typescript-eslint/eslint-parser与版本类似4.x.x。
哇!那是一段相当长的旅程,但希望这能回答您的问题!:)
根据评论编辑:
现在假设我分叉了@typescript-eslint/eslint-plugin 并且想要所有的 ERR!有问题的消息消失。如果我将 eslint 和解析器添加到分叉包的依赖项中, peerDependencies 变得毫无意义。我应该将它们添加到 devDependencies 中吗?
你可以,但这会使devDependencies意义变得毫无意义。您必须了解的是,该package.json文件只是一个清单,用于指示 NPM 在其他人“安装”该包时要执行的操作 - 无论是在另一个包中作为依赖项,还是单独作为全局包。无论如何,这package.json就像 NPM 的说明手册。
它不会以任何方式影响您作为开发人员。因此,如果您只想添加eslint并@typescript-eslint/eslint-parser用于开发目的,您可以简单地执行以下操作:
$ npm install --no-save eslint @typescript-eslint/eslint-parser
该--no-save标志告诉 NPM 不要将这些添加到 ,package.json而是node_modules/无论如何获取包并将其放入目录中。当你运行你的应用程序时,它会做的就是查看node_modules/包的存在而不是package.json。的目的package.json是在安装步骤之后完成。
如果这能澄清您的问题,请告诉我。如果需要,我会添加更多。
新年快乐!:)