我应该在 "package.json" 的 "dependencies" 字段中复制 "peerDependencies" 吗?
Should I duplicate "peerDependencies" in "dependencies" field of "package.json"?
为了实验,我下载了@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"
},
}
如果我 运行 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
想要:
{
"peerDependencies": {
"@typescript-eslint/parser": "^4.0.0",
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
},
"dependencies": {
"@typescript-eslint/parser": "^4.0.0",
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
// ...
}
}
peerDependencies
字段旨在与 libraries/plugins 一起使用,作为一种让正在安装的应用程序知道其工作需要哪些依赖项的方式,而无需在 dependencies
字段.
来自docs:
As a package manager, a large part of npm's job when installing your dependencies is managing their versions. But its usual model, with a "dependencies" hash in package.json, clearly falls down for plugins. Most plugins never actually depend on their host package, i.e. grunt plugins never do require("grunt")
, so even if plugins did put down their host package as a dependency, the downloaded copy would never be used. So we'd be back to square one, with your application possibly plugging in the plugin to a host package that it's incompatible with.
想法是您在 devDependencies
中安装包以开发包并发布它 没有 该依赖项,然后任何尝试使用您的包的应用程序和没有安装此 peer-dependency 将收到错误:
npm ERR! peerinvalid The package flatiron does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer flatiron-cli-config@0.1.3 wants flatiron@~0.1.9
npm ERR! peerinvalid Peer flatiron-cli-users@0.1.4 wants flatiron@~0.3.0
您收到的错误只是说 @typescript-eslint/eslint-plugin
需要您安装 eslint
才能正常工作。
所以,显而易见的答案是 运行 npm i -D eslint
将其保存为开发依赖项。 但是,这个插件是typescript-eslint
包的子目录,贡献者忘记添加eslint
作为开发依赖的可能性似乎不大,所以,可以肯定地说,开发不需要安装它。
不知道它在内部是如何工作的,我会说,因为使用 @typescript-eslint/eslint-plugin
需要 typescript-eslint
,为父包开发任何插件都需要通过父包本身来完成。
如果您查看 contribution-guide,它提到从根目录开发:
Developing in this repo is easy:
- First fork the repo, and then clone it locally.
- Create a new branch.
- In the root of the project, run yarn install.
- This will install the dependencies, link the packages and do a build.
- Make the required changes.
我不是专家,所以对此持保留态度,但是,与其直接在子目录中工作,我认为您需要从项目的根目录开始处理插件。
@Daniel_Knights 基本中肯地回答了问题。但我也想加上我的两分钱。所以这里是:
NPM 中的依赖类型:
为了理解这一点,了解 NPM 包中不同类型的依赖关系很重要。一般来说,npm中有4种依赖:
直接依赖(或简称依赖):这些依赖对于NPM包来说是绝对必要的功能。如果您正在使用 express.js 构建 Web 应用程序,那么您绝对希望安装 express
以便您的应用程序启动。所以这将是您的应用程序的直接依赖项。这些应列在 package.json
.
的 "dependencies": {}
部分下
开发依赖项:这些依赖项在开发您的应用程序时很有帮助,但应用程序包不一定使用到运行。这种依赖的一个例子是 typescript
。 NodeJS 不理解 Typescript。因此,即使您可以使用 Typescript 编写应用程序,但在通过 Typescript 编译器 运行 之后,您将得到 Javascript。因此,即使您需要在开发过程中添加 typescript
包,您的应用程序在编译后也不需要它 运行。
因此,如果您将 typescript
添加到 package.json
中的 "devDependencies": {}
部分并执行 npm install
,NPM 将同时安装依赖项和 devDependencies。在此阶段,您可以调用您的 Typescript 编译器来构建您的应用程序。但在那之后,你可以 运行 npm prune --production
,NPM 将从 node_modules/
中剥离所有 devDependencies。这会减少您的最终应用程序包大小,并使其不受任何开发依赖。
你不应该在你的源代码中引用任何开发依赖,而不允许你的代码安全和优雅地回退到替代方案,因为包将在 p运行ing.
- 可选依赖项:这些是您可以在
package.json
的 "optionalDependencies": {}
部分中指定的依赖项。当您将依赖项指定为可选时,您让 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 也会尝试安装一个可选的依赖项。但是如果它无法安装(可能是因为本机绑定不可用),它不会出错。它只是 post 一个警告然后继续。
现在讨论所讨论的依赖类型...
- 对等依赖性:如您所知,这些是您在
package.json
的 "peerDependencies": {}
部分中指定的依赖性。与上面的其他三个依赖项不同,NPM 在执行 npm install
时不会尝试安装 对等依赖项。这是因为 NPM 期望这些依赖项由 other 依赖项提供。
我们会明白为什么这是有道理的,但我们必须花很短的时间来了解 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 ...
现在,express/
文件夹中没有 node_modules/
文件夹,尽管它有一个 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
如果你查看 express
包的 package.json
,你会发现它需要版本为 6.7.0
:[=98= 的 qs
包]
$ 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
但版本为 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
。但是 6.7.0
需要 qs
的 express
包的需求呢?别担心,NPM 通过在 6.7.0
.
处提供 express
它自己的 qs
本地副本来处理它
$ 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单独为express
添加了本地node_modules
,并且给出了自己的版本。这就是 NPM 确保我们的应用程序以及 express
都满足他们自己的要求的方式。但这里有一个关键要点:
“如果多个包需要另一个相同但版本不同的包,NPM 将为每个包安装多个副本以满足它们。”。 =189=]
在某些情况下,这可能并不总是理想的。假设我们的包想要使用 qs
但我们不关心它是什么版本,只要它高于版本 6.0.0
并且我们确信其他一些包,如 express
会也可以一起使用(在 6.7.0
处有自己的 qs
)。在这种情况下,我们可能不希望 NPM 安装另一个增加体积的副本。相反,我们可以将 qs
指定为...peer dependency!
现在 NPM 不会自动安装对等依赖项。但会期望它由其他包提供。
所以最后,来到你的案例...
在@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"
},
}
@typescript-eslint/eslint-plugin
旨在与 @typescript-eslint/parser
和 eslint
包一起使用。如果不使用这些,你就无法使用 @typescript-eslint/eslint-plugin
,因为所有这些都是更大的包 eslint
的一部分,它可以帮助你检查 Typescript 和 JS 代码。所以你无论如何都会安装 eslint
并且那将是使用 @typescript-eslint/eslint-plugin
.
的唯一原因
因此,作者认为添加它们是合适的,因为 @typescript-eslint/eslint-plugin
并不关心,只要您在 5.x.x
、[=82] 中有 eslint
的任何次要版本=] 或 7.x.x
系列。对于 @typescript-eslint/eslint-parser
版本 4.x.x
.
同样
哇!这是一个很好的旅程,但希望这能回答你的问题! :)
根据评论编辑:
Now assume that I forked the @typescript-eslint/eslint-plugin and want all ERR! messages mentioned in question disappear. If I add eslint and parser to dependencies of forked package, peerDependencies becomes meaningless. Should I add them to devDependencies instead?
可以,但那样会使 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
的目的是在安装步骤之后完成。
如果这能澄清您的问题,请告诉我。如果需要我会添加更多。
新年快乐! :)
为了实验,我下载了@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"
},
}
如果我 运行 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
想要:
{
"peerDependencies": {
"@typescript-eslint/parser": "^4.0.0",
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
},
"dependencies": {
"@typescript-eslint/parser": "^4.0.0",
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
// ...
}
}
peerDependencies
字段旨在与 libraries/plugins 一起使用,作为一种让正在安装的应用程序知道其工作需要哪些依赖项的方式,而无需在 dependencies
字段.
来自docs:
As a package manager, a large part of npm's job when installing your dependencies is managing their versions. But its usual model, with a "dependencies" hash in package.json, clearly falls down for plugins. Most plugins never actually depend on their host package, i.e. grunt plugins never do
require("grunt")
, so even if plugins did put down their host package as a dependency, the downloaded copy would never be used. So we'd be back to square one, with your application possibly plugging in the plugin to a host package that it's incompatible with.
想法是您在 devDependencies
中安装包以开发包并发布它 没有 该依赖项,然后任何尝试使用您的包的应用程序和没有安装此 peer-dependency 将收到错误:
npm ERR! peerinvalid The package flatiron does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer flatiron-cli-config@0.1.3 wants flatiron@~0.1.9
npm ERR! peerinvalid Peer flatiron-cli-users@0.1.4 wants flatiron@~0.3.0
您收到的错误只是说 @typescript-eslint/eslint-plugin
需要您安装 eslint
才能正常工作。
所以,显而易见的答案是 运行 npm i -D eslint
将其保存为开发依赖项。 但是,这个插件是typescript-eslint
包的子目录,贡献者忘记添加eslint
作为开发依赖的可能性似乎不大,所以,可以肯定地说,开发不需要安装它。
不知道它在内部是如何工作的,我会说,因为使用 @typescript-eslint/eslint-plugin
需要 typescript-eslint
,为父包开发任何插件都需要通过父包本身来完成。
如果您查看 contribution-guide,它提到从根目录开发:
Developing in this repo is easy:
- First fork the repo, and then clone it locally.
- Create a new branch.
- In the root of the project, run yarn install.
- This will install the dependencies, link the packages and do a build.
- Make the required changes.
我不是专家,所以对此持保留态度,但是,与其直接在子目录中工作,我认为您需要从项目的根目录开始处理插件。
@Daniel_Knights 基本中肯地回答了问题。但我也想加上我的两分钱。所以这里是:
NPM 中的依赖类型:
为了理解这一点,了解 NPM 包中不同类型的依赖关系很重要。一般来说,npm中有4种依赖:
直接依赖(或简称依赖):这些依赖对于NPM包来说是绝对必要的功能。如果您正在使用 express.js 构建 Web 应用程序,那么您绝对希望安装
的express
以便您的应用程序启动。所以这将是您的应用程序的直接依赖项。这些应列在package.json
."dependencies": {}
部分下开发依赖项:这些依赖项在开发您的应用程序时很有帮助,但应用程序包不一定使用到运行。这种依赖的一个例子是
typescript
。 NodeJS 不理解 Typescript。因此,即使您可以使用 Typescript 编写应用程序,但在通过 Typescript 编译器 运行 之后,您将得到 Javascript。因此,即使您需要在开发过程中添加typescript
包,您的应用程序在编译后也不需要它 运行。
因此,如果您将 typescript
添加到 package.json
中的 "devDependencies": {}
部分并执行 npm install
,NPM 将同时安装依赖项和 devDependencies。在此阶段,您可以调用您的 Typescript 编译器来构建您的应用程序。但在那之后,你可以 运行 npm prune --production
,NPM 将从 node_modules/
中剥离所有 devDependencies。这会减少您的最终应用程序包大小,并使其不受任何开发依赖。
你不应该在你的源代码中引用任何开发依赖,而不允许你的代码安全和优雅地回退到替代方案,因为包将在 p运行ing.
- 可选依赖项:这些是您可以在
package.json
的"optionalDependencies": {}
部分中指定的依赖项。当您将依赖项指定为可选时,您让 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 也会尝试安装一个可选的依赖项。但是如果它无法安装(可能是因为本机绑定不可用),它不会出错。它只是 post 一个警告然后继续。
现在讨论所讨论的依赖类型...
- 对等依赖性:如您所知,这些是您在
package.json
的"peerDependencies": {}
部分中指定的依赖性。与上面的其他三个依赖项不同,NPM 在执行npm install
时不会尝试安装 对等依赖项。这是因为 NPM 期望这些依赖项由 other 依赖项提供。
我们会明白为什么这是有道理的,但我们必须花很短的时间来了解 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 ...
现在,express/
文件夹中没有 node_modules/
文件夹,尽管它有一个 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
如果你查看 express
包的 package.json
,你会发现它需要版本为 6.7.0
:[=98= 的 qs
包]
$ 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
但版本为 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
。但是 6.7.0
需要 qs
的 express
包的需求呢?别担心,NPM 通过在 6.7.0
.
express
它自己的 qs
本地副本来处理它
$ 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单独为express
添加了本地node_modules
,并且给出了自己的版本。这就是 NPM 确保我们的应用程序以及 express
都满足他们自己的要求的方式。但这里有一个关键要点:
“如果多个包需要另一个相同但版本不同的包,NPM 将为每个包安装多个副本以满足它们。”。 =189=]
在某些情况下,这可能并不总是理想的。假设我们的包想要使用 qs
但我们不关心它是什么版本,只要它高于版本 6.0.0
并且我们确信其他一些包,如 express
会也可以一起使用(在 6.7.0
处有自己的 qs
)。在这种情况下,我们可能不希望 NPM 安装另一个增加体积的副本。相反,我们可以将 qs
指定为...peer dependency!
现在 NPM 不会自动安装对等依赖项。但会期望它由其他包提供。
所以最后,来到你的案例...
在@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"
},
}
@typescript-eslint/eslint-plugin
旨在与 @typescript-eslint/parser
和 eslint
包一起使用。如果不使用这些,你就无法使用 @typescript-eslint/eslint-plugin
,因为所有这些都是更大的包 eslint
的一部分,它可以帮助你检查 Typescript 和 JS 代码。所以你无论如何都会安装 eslint
并且那将是使用 @typescript-eslint/eslint-plugin
.
因此,作者认为添加它们是合适的,因为 @typescript-eslint/eslint-plugin
并不关心,只要您在 5.x.x
、[=82] 中有 eslint
的任何次要版本=] 或 7.x.x
系列。对于 @typescript-eslint/eslint-parser
版本 4.x.x
.
哇!这是一个很好的旅程,但希望这能回答你的问题! :)
根据评论编辑:
Now assume that I forked the @typescript-eslint/eslint-plugin and want all ERR! messages mentioned in question disappear. If I add eslint and parser to dependencies of forked package, peerDependencies becomes meaningless. Should I add them to devDependencies instead?
可以,但那样会使 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
的目的是在安装步骤之后完成。
如果这能澄清您的问题,请告诉我。如果需要我会添加更多。
新年快乐! :)