为了实验,我下载了@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"
// ...
}
}
2条答案
按热度按时间elcex8rz1#
@丹尼尔_Knights回答了这个问题。但我也想补充我的两分钱。所以这里是:
NPM中的依赖类型:
为了理解这一点,重要的是要理解NPM包中不同类型的依赖关系。通常,NPM中有4种类型的依赖关系:
1.直接依赖(或简称依赖):这些是NPM包运行所必需的依赖项。如果您正在使用express.js构建Web应用程序,那么您绝对希望安装
express
包以 Boot 您的应用程序。因此,这将是您的应用程序的直接依赖项。这些应该在package.json
的"dependencies": {}
部分列出。1.开发依赖:这些依赖关系在开发应用程序时很有帮助,但不一定会被应用程序包使用来运行。这种依赖关系的一个例子是
typescript
。NodeJS不理解Typescript。因此,即使您可以使用Typescript编写应用程序,在通过Typescript编译器运行后,所以即使你需要在开发过程中添加typescript
包,你也不需要它来让你的应用程序在编译后运行。因此,如果您将
typescript
添加到package.json
中的"devDependencies": {}
部分并执行npm install
,NPM将安装依赖项和devDependencies。在此阶段,您可以调用Typescript编译器来构建应用程序。但在此之后,您可以运行npm prune --production
,NPM将从node_modules/
中剥离所有devDependencies。这减少了最终应用程序包的大小,并使其不受任何dev依赖项的影响。您不应该在源代码中引用任何dev依赖项,而不允许您的代码安全而优雅地回退到替代方案,因为该包将在修剪时被删除。
1.可选依赖:这些是你可以在
package.json
的"optionalDependencies": {}
部分指定的依赖项。当你指定一个依赖项为可选时,你让NPM知道 “如果它可用,你的程序将使用这个依赖项。如果它不可用,那也很酷。它将使用其他东西。”一个常见的情况是使用数据库驱动程序,用JS编写的数据库驱动程序不是特别高效,所以通常使用带有本地绑定的驱动程序(使用原生(C/C++)包来运行其任务的JS库)。但问题是对于原生绑定,native包必须安装在运行应用程序的机器上。这可能并不总是可用的。所以我们可以指定一个native库作为可选库。您可以在JS代码中引用它,如:
因此,当使用
npm install
安装软件包时,NPM也会尝试安装一个可选的依赖项。但是如果它无法安装(可能是因为本机绑定不可用),它不会出错。它只会发布一个警告并继续。现在我们来谈谈这种依赖性。
1.对等依赖:正如你已经知道的,这些是你在
package.json
的"peerDependencies": {}
部分中指定的依赖。与上面的其他三个依赖不同,NPM在执行npm install
时不会尝试安装对等依赖。这是因为NPM期望这些依赖由 other 依赖提供。我们将看到为什么这是有意义的,但我们必须采取一个非常短的弯路,以了解NPM如何在
node_modules/
文件夹中构建依赖关系。NPM如何存储依赖
让我们用一个例子来做这件事:
我们将初始化一个npm包并安装
express
作为依赖项:如果我们现在查看
node_modules/
目录,我们可以看到它已经安装了qs
包沿着express
:现在,
express/
文件夹中没有node_modules/
文件夹,尽管它有一个package.json
:如果您查看
express
包的package.json
,您会看到它需要qs
包,版本为6.7.0
:因此
express
需要qs
版本6.7.0
,因此NPM将其放在express
旁边供其使用。现在让我们看看如果我们想在我们的应用程序中使用
qs
,但版本为6.8.0
,会发生什么。NPM用我们想要的
6.8.0
替换了这个版本。但是express
包需要qs
在6.7.0
上呢?不用担心,NPM通过给express
提供qs
在6.7.0
上的本地副本来解决这个问题。所以你可以看到NPM单独为
express
添加了一个本地node_modules
,并给出了自己的版本。这就是NPM如何确保我们的应用程序以及express
都满足自己的要求。但这里有一个关键的要点:***“如果多个软件包需要另一个通用但版本不同的软件包,NPM将为每个软件包安装多个副本,以满足它们。
在某些情况下,这可能并不总是理想的。假设我们的软件包想要使用
qs
,但我们不关心它是什么版本,只要它高于6.0.0
版本,并且我们确信其他一些软件包(如express
)也会一起使用(它在6.7.0
上有自己的qs
)。在这种情况下,我们可能不希望NPM安装另一个副本增加批量。相反,我们可以指定qs
作为...peer dependency!现在,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
的唯一原因。因此,作者认为添加themn是合适的,因为只要您在
5.x.x
,6.x.x
或7.x.x
系列中有eslint
的任何次要版本,@typescript-eslint/eslint-plugin
就不在乎。唷!那是一段相当长的旅程,但希望这回答了你的问题!:)
根据评论编辑:
现在假设我fork了@typescript-eslint/eslint-plugin,并希望所有提及的ERR!消息消失。如果我将eslint和parser添加到fork包的依赖项中,peerDependencies变得毫无意义。我应该将它们添加到devDependencies中吗?
你可以这样做,但是这样做会使
devDependencies
变得毫无意义。你必须明白的是,package.json
文件只是一个清单,用来指示NPM在其他人“安装”这个包时该怎么做-无论是作为依赖项安装在另一个包中,还是单独作为全局包安装。无论如何,
package.json
就像是NPM的指导手册。它不会以任何方式影响您作为开发人员。因此,如果您想做的只是添加eslint
和@typescript-eslint/eslint-parser
用于开发目的,您可以简单地执行以下操作:--no-save
标志告诉NPM不要将这些添加到package.json
中,而是获取包并将其放在node_modules/
目录中。当您运行应用程序时,它所做的就是查看node_modules/
中的包,而不是package.json
。package.json
的目的是在安装步骤之后完成。如果这澄清了你的问题,请告诉我。如果需要,我会补充更多。
新年快乐!
w8rqjzmb2#
peerDependencies
字段旨在与库/插件一起使用,作为一种让安装应用知道需要哪些依赖项才能工作的方式,而不会在dependencies
字段中向包本身添加额外的批量。关于docs:
作为一个包管理器,在安装依赖项时,npm的大部分工作是管理它们的版本。但是它通常的模型,在package.json中使用“dependencies”散列,显然福尔斯于插件。大多数插件实际上从不依赖于它们的主机包,即grunt插件从不做
require("grunt")
,所以即使插件确实将其主机包作为依赖项,下载的副本将永远不会被使用。所以我们将回到起点,您的应用程序可能会将插件插入到与之不兼容的主机包中。这个想法是,你在
devDependencies
中安装这个包来开发这个包,然后发布它没有这个依赖,然后任何试图使用你的包并且没有安装这个对等依赖的应用程序都会收到一个错误:您收到的错误只是说
@typescript-eslint/eslint-plugin
需要您安装eslint
才能正常工作。因此,显而易见的答案是运行
npm i -D eslint
将其保存为dev-dependency。但是,此插件是typescript-eslint
包的子目录,贡献者忘记将eslint
添加为dev-dependency的可能性似乎不太可能,因此,可以安全地假设,它不需要安装用于开发。在不知道它内部如何工作的情况下,我会说,由于
typescript-eslint
是使用@typescript-eslint/eslint-plugin
所必需的,因此为父包开发任何插件都需要通过父包本身进行。如果你看一下contribution-guide,它提到了从根目录开发:
在这个repo中开发很容易:
我不是Maven,所以对此持保留态度,但是,与其直接在子目录中工作,我认为您需要从项目的根目录中处理插件。