npm package.json 字段梳理
好久没更新博客了拿着很久之前记的笔记凑个更新吧。
package.json,一个前端项目的归属——万万没想到的是,它有很多学问,因此现在我们就来完整的盘点一下。
- name
包名,全世界唯一 - version
版本号,怎么打请见「npm version」,做个人,请遵循语义化版本号 - description
描述 - keywords
string[]
和描述类似 - homepage
包首页,可能是文档网站或者 GitHub 地址 - bugs
where is bug list,可以放 issue 地址,npm bug
可以打开地址(说实话我第一次知道这个指令) - licenses / license
许可协议,可以是多个:https://spdx.org/licenses/ - author/contributors
作者信息
{
"name" : "Barney Rubble",
"email" : "b@rubble.com",
"url" : "http://barnyrubble.tumblr.com/"
}
// 或者合起来
{
"author": "Barney Rubble <b@rubble.com> (http://barnyrubble.tumblr.com/)"
}
- funding
在哪里打钱给你 files
包发布后包含的文件,遵循.gitignore
同款语法,默认会包含全部,这里复杂而日狗的是,npm 会使用三份内容:files
字段,.npmignore
,.gitignore
,以及背后的一些包含关系.npmignore
在根目录时不会覆盖 files 字段;在子目录时会- 如果没有
.npmignore
,会用上.gitignore
- 在
files
字段中的包含的内容无法被.npmignore
和.gitignore
覆盖 以下内容一定会包含
package.json
README
LICENSE
/LICENCE
main
字段中的文件
以下内容一定会被忽略
.git
CVS
.svn
.hg
.lock-wscript
.wafpickle-N
.*.swp
.DS_Store
._*
npm-debug.log
.npmrc
node_modules
config.gypi
.orig
package-lock.json
(如果你需要请使用 npm-shrinkwrap)
- main
入口文件,当你require('module')
时,就会使用这个入口文件。默认会取index.js
,所有路径都是相对于你的项目根目录而言的。 - browser
如果你的包在浏览器环境下使用,应该使用这个字段表明浏览器里用哪个文件,他可以提示用户这个文件不能在 Node.js 下使用。(注意,这里依旧是 require 时使用,所以提供的不应该是iife
,而是umd
) - bin
对于 cli 项目,你可能希望把文件安装进 bin 目录方便使用,这个时候就会用到这个字段,如果bin: string
,那么表示指明bin
文件应该是哪个;如果定义为:
{
"bin": {
// 同样的,这个方法可以注册多个可执行文件
"myapp": "./cli.js"
}
}
那么代表会在 bin 目录里增加一个叫 myapp
可执行文件,同时为了让 node 成功执行,记得在 bin 文件头上加 #!/usr/bin/env node
- man
文档地址,和 doc 不同的是,我们都知道 Linux 有个 man 命令,他其实是用于注册 man [myapp] 这样的 man 文件的配置项
- directories
表明包结构,主要有两个子配置,看文档
- repository
仓库信息,品种齐全,价格丰富
{
"repository": "npm/npm",
"repository": "github:user/repo",
"repository": "gist:11081aaa281",
"repository": "bitbucket:user/repo",
"repository": "gitlab:user/repo"
}
- scripts
这我只能说,你怎么这么熟练。
但其实需要注意的是,它其实是有生命周期钩子的:
{
"scripts": {
"precompress": "{{ executes BEFORE the `compress` script }}",
"compress": "{{ run command to compress files }}",
"postcompress": "{{ executes AFTER `compress` script }}"
}
}
此外,还提供了包全局的生命周期钩子:prepare
, prepublish
, prepublishOnly
, prepack
, postpack
- config
可能要试验下
dependencies
语法很多很花哨,需要注意区分 devDependencies 和 peerDependencies
- dependencies:使用这个包需要安装
- devDependencies:开发这个包的时候安装,使用者不需要关心
peerDependencies:我可能不想集成进我自己的包,但需要你本机装了的包
- peerDependenciesMeta:依赖说明,比如他可能并不非得要,可以标记成 optional
- optionalDependencies:这个库可以有,但没有也能用
- bundledDependencies:没怎么看懂,需要在研究一下
- overrides
日我依赖的依赖,直呼牛逼
- engines
我支持的 node / npm 版本,会在安装包时检查
- os
支持安装的平台
- cpu
支持的 CPU 类型
- private
不许发
- publishConfig
- workspaces
一个 Monorepo 概念
以上是 npm 中的说明,在 Node 中 package.json 还有一些扩充,它和 npm 的主要区别是,npm 列出的是 npm 用到的,而 node 列出的是由 Node 去控制使用的。
- type
表明包是
esm
还是commonjs
;如果引入方式是import
且文件以 mjs 结尾;或者 package.json 中type: module
时,JS 文件会被判断成esm
使用的文件;否则会当成commonjs
处理。注意:和
.mjs
一致,.cjs
永远作为commonjs
处理 - packageManager
指定的包管理器,详情见文档
exports
一个新增加的很强的东西,在 npm 中我们说明了 main 是入口文件,控制引入的默认文件;而
exports
则可以控制:- 只允许引入后访问我指定的
exports
list 入口点 - 支持在不同环境使用不同入口文件
- 只允许引入后访问我指定的
{
"name": "my-mod",
// 只能用下面这些
"exports": {
".": "./lib/index.js",
"./lib": "./lib/index.js",
"./lib/index": "./lib/index.js",
"./lib/index.js": "./lib/index.js",
"./feature": "./feature/index.js",
"./feature/index.js": "./feature/index.js",
"./package.json": "./package.json"
}
}
exports 的优先级比 main 更高,此时 main 可以当做低版本 node 的降级策略去使用。
条件导出也就是之前说的不同环境,不同入口文件,这里几个简单的 case:
"exports": {
"import": "./main-module.js",
"require": "./main-require.cjs"
}
分别指定了 esm 和 commonjs 的情况下使用的文件,支持以下参数:
"node"
- 匹配任何 Node.js 环境。 可以是 CommonJS 或 ES 模块文件。 在大多数情况下,不需要明确调用 Node.js 平台。"import"
- 当包通过import
或import()
,或者通过 ECMAScript 模块加载器的任何顶层导入或解析操作加载时匹配。 无论目标文件的模块格式如何,都适用。 始终与"require"
互斥。"require"
- 当包通过require()
加载时匹配。 引用的文件应该可以用require()
加载,尽管无论目标文件的模块格式如何,条件都匹配。 预期的格式包括 CommonJS、JSON 和原生插件,但不包括 ES 模块,因为require()
不支持它们。 始终与"import"
互斥。"default"
- 始终匹配的通用后备。 可以是 CommonJS 或 ES 模块文件。 此条件应始终放在最后。
注意,node 字段也同样支持套娃:
"node": {
"import": "./feature-node.mjs",
"require": "./feature-node.cjs"
}
当然,双包问题是另一个问题:
还没有仔细研究,大致讲的是由于不同引入方式引入了不同文件导致的不一致性甚至是跑不起来的情况,用隔离的 exports 可以最大程度避免这个问题。
最后,再来看看 TypeScript 怎么支持的 node esm ,以及他加了啥:
Documentation - Experimental Support for ECMAScript Modules in Node.js
- types
定义了类型文件;exports 中也同样有了新的 types,根中的 types 作为一个 fallback 可。
这样我们就解释了「我拿到的包为什么不对」,以及「谁负责判断我用哪个包」,而「双包问题」,可能是下一次的 topic 了。
植入部分
如果您觉得文章不错,可以通过赞助支持我。
如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。