package-lock.json
一个清单的清单表现形式
描述
对于NPM修改node_modules
树或package.json
的任何操作,都会自动生成package-lock.json
。
这个文件被提交到源仓库中,并用于各种目的:
- 描述依赖关系树的单一表示,这样团队成员、部署和持续集成就可以保证安装完全相同的依赖关系。
- 为用户提供一种“时间旅行”到
node_modules
以前的状态的工具,而无需提交目录本身。 - 通过可读的源代码控制差异,让树变更(tree changes)有更好的可见性。
- 优化安装过程,允许npm跳过已经安装过的包的重复元数据解析。
- 从npm v7开始,lockfiles包含了足够的信息来获得包树(package tree)的完整结构,从而减少了读取
package.json
文件的需要,并显著的提升了性能。
package-lock.json 与 npm-shrinkwrap.json对比
这两个文件具有相同的格式,并在项目的根目录中执行类似的功能。
不同之处在于package-lock.json
不能被发布,如果在根目录之外的任何地方发现它将被忽略。
相比之下,npm-shrinkwrap.Json
允许发布,并从遇到的地点定义依赖树。除非部署CLI工具或使用发布过程(publication process)来生成生产包(production packages),否则不建议这样做。
如果package-lock.json
和npm-shrinkwrap.json
同时出现在项目的根目录中,npm-shrinkwrap.json
优先生效,package-lock.json
将被忽略。
被隐藏的 Lockfiles
为了避免重复处理node_modules文件夹,从v7开始,npm使用node_modules/.package-lock.json
中“隐藏”的lockfile。它包含关于树的信息,并用于代替读取整个node_modules
层次结构,前提是满足以下条件:
- Lockfile引用到的所有包文件夹都在
node_models
目录层次结构中 - Lockfile中没有列出的包文件夹都不在
node_models
目录层次结构中 - 文件的修改时间至少与它引用的所有包文件夹的更新时间相同。
也就是说,如果隐藏的lockfile是作为软件包树的最新更新的一部分创建的,则该文件才具有相关性。如果另一个CLI以任何方式对该树进行了变异,则会检测到该异常,并且隐藏的锁定文件将被忽略。
请注意,当手动更改包的内容时,包文件夹的修改时间不受影响(不会改变)。例如,如果将一个文件添加到node_modules/foo/lib/bar.js
中,那么node_module/foo
上的修改时间将不会反映这种变化。如果您在node_module中手动编辑文件,通常最好是删除node_modules/.package-lock.json
。
由于隐藏的lockfile被较早的npm版本忽略,它在普通的lockfile("normal" lockfiles.)中不存在向后兼容性。也就是说,它是lockfileVersion:3,而不是lockfileVersion:2。
处理旧版本Lockfiles
当npm在包安装过程中,检测到一个来自npm v6或更早版本的lockfile文件时,它会自动更新,从node_modules树或npm registry(当node_modules树是空的或者是一个特别老版本的lockfile)中获取丢失的信息。
文件格式
name
包的名字。也为package-lock提供名字。这将匹配package.json中的内容。
version
包的版本号。也为package-lock提供版本。这将匹配package.json中的内容。
lockfileVersion
一个整数版本,从文档的版本号1开始,其语义在生成这个package-lock.json时被使用。注意,在npm v7中,文件格式发生了很大的变化,以跟踪之前需要在node_modules或npm注册表中查找的信息。npm v7生成的lockfile将包含lockfileVersion: 2。
- 没有提供版本:一个来自npm v5之前的npm版本的“古老”shrinkwrap文件。
1
:npm v5和v6使用的lockfile版本。2
:npm v7使用的lockfile版本,向后兼容v1 lockfile。3
:npm v7使用的lockfile版本,没有向后兼容性。这用于node_modules/.package-lock.josn
中的隐藏lockfile。一旦不再支持npm v6,就可能在npm的未来版本中使用。
NPM总是试图从lockfile中获取它所能获取的任何数据,即使它不是被支持的版本。
packages
这是一个对象,将包位置映射到另一个包含该包信息的对象。
工程的根路径通常以""
键列出,所有其他包都可以用它们与工程的根路径文件夹的相对路径列出。
包描述符有以下字段:
-
version
:在package.json中的版本 -
resolved
:实际包被解析的位置。在从注册表(registry)获取包的情况下,这将是一个tarball
的url。在git依赖的情况下,这将是完整的带有commit sha值的git url。在链接依赖项的情况下,这将是链接目标的位置。 -
integrity
:一个sha512或sha1标准子资源完整性字符串,用于在此位置解包的工作。 -
link
:表示这是一个符号链接的标志。如果存在,则不指定其他字段,因为链接目标也将包含在lockfile中。 -
dev, optional, devOptional
:如果包是严格的devDependencies树的一部分,那么dev
将为true
。如果严格来说它是optionalDependencies树的一部分,那么optional
将被设置。如果它既是一个开发依赖,又是一个非开发依赖的可选依赖,那么将设置devOptional
。(dev依赖项的可选依赖项将同时设置dev
和optional
为true
) -
inBundle
: 一个标志,说明包是一个绑定的依赖项(bundled dependency) -
hasInstallScript
: 一个标志,说明包带有preinstall
,install
, 或者postinstall
脚本. -
hasShrinkwrap
:一个标志,说明包带有npm-shrinkwrap.json
文件 -
bin, license, engines, dependencies, optionalDependencies:
:来自package.json的字段
dependencies
支持使用lockfileVersion:1
的npm版本的历史遗留数据。这是包名到依赖项对象的映射。因为对象结构是严格分层的,所以符号链接依赖关系在某些情况下表示起来有些困难。
如果有packages
部分,NPM v7会完全忽略这个部分,但为了支持在NPM v6和NPM v7之间切换,它会保持更新。
dependencies
对象包含以下字段:
version
: 根据包的性质而变化的说明符,并且在获取包的新副本时可用。
1.bundled dependencies
:不管来源是什么,这只是一个纯粹用于提供信息的版本号。
2.registry sources
:这是一个版本号。(如1.2.3)
3.git sources
:这是一个带有resolved committish的git说明符。(git+https://example.com/foo/bar#115311855adb0789a0466714ed48a1499ffea97e)
4.http tarball sources
:这是一个tarball的url(如:https://example.com/example-1.3.0.tgz)
5.local tarball sources
:这是一个tarball的文件url(如:file:///opt/storage/example-1.3.0.tgz)
6.local link sources
:这是一个链接的文件URL(如:file:libs/our-module)integrity
:一个标准子资源完整性字符串的sha512或sha1值,用于在此位置解包的工件。对于git依赖,这是commit sha。resolved
: 对于注册表来源,这是tarball相对于注册表URL的路径。如果tarball URL与注册中心URL不在同一服务器上,那么这就是一个完整的URL。bundled
:如果为true,这是绑定的依赖项,将由父模块安装。在安装时,这个模块将在提取阶段从父模块中提取出来,而不是作为一个单独的依赖项安装。dev
:如果为true
,那么这个依赖关系要么是顶级模块的开发依赖关系,要么是顶级模块的传递依赖关系。对于既属于顶级的开发依赖关系,又属于顶级的非开发依赖关系的传递依赖关系,这是false
。optional
:如果为true
,那么这个依赖关系要么是顶级模块的开发依赖关系,要么是顶级模块的传递依赖关系。对于既属于顶级的开发依赖关系,又属于顶级的非开发依赖关系的传递依赖关系,这是false
。requires
:这是模块名称到版本的映射。这是这个模块需要的所有东西的列表,而不管它将被安装在哪里。版本应该通过正常的匹配规则匹配我们的依赖项或更高级别的依赖项。dependencies
:这个依赖关系的依赖关系,就像在顶层一样。