package.json 属性讲解
一. 前言
之所以写下这篇文,是因为前两天忽然发现,package.json 里的 **版本号 **我好像就没注意过了,一会 ~ 开头,一会 ^ 开头,都是个啥?然后仔细回想了下发现我好像并没有真的了解过这玩意, 每次打开个项目都是 npm install 一波敲敲启动命令就完事了。事后深刻反思了下觉得这样并不好 万一某天来个人问你这里的XX是啥意思你都说不上来那多尴尬,于是就有了这篇文。
本文档是自己看官方文档的理解 + 翻译,内容是
package.json配置里边的属性含义。package.json必须是一个严格的json文件,而不仅仅是js里边的一个对象。其中很多属性可以通过npm-config来生成。
1. npm
在介绍 package.json 前,需要先介绍一下 npm。npm 就是一个包含了很多 Javascript 包(package)的公有仓库(registry),开源开发者们可以贡献包,也可以在自己的项目中下载包。
有关 package 和 modules 的描述可以查看官方文档(About packages and modules (opens new window))(这波懒得翻译了)
2. package.json
package.json 是用来管理包的。要将一个包发布到npm中,必须要包含一个package.json文件。
package.json 文件的作用:
- 列举出你的项目所依赖的包;
 - 明确你的项目中可以使用的包的版本;
 - 让你的包能够进行二次开发,更加容易和其他开发者分享;
 
3. mkdir package.json
创建方式有两种,通过命令行创建或者直接创建默认的 package.json 文件。
命令行: 进入项目目录 → npm init,然后通过命令行交互进行创建即可。
自定义创建 package.json 的命令行交互:可参考 init-package-json
创建默认文件:可以使用当前目录内抽取的信息直接生成默认的 package.json 文件。进入项目目录 → npm init --yes | npm init -y
我们也可以配置初始化命令的配置项来设置默认值:
> npm set init.author.email "[email protected]"
> npm set init.author.name "example_user"
> npm set init.license "MIT"
二. 配置项属性
1. name
package.json 中最重要的属性是 name 和 version 两个属性,这两个属性是必须要有的,否则模块就无法被安装,这两个属性一起形成了一个 npm 模块的唯一标识符 。模块中内容变更的同时,模块版本也应该一起变化。
如果你要发布你的包,那么配置 name 和 version 字段是必须要填写且十分重要的;如果并不需要发布,那么 name 和 version 是可选的。
对于 name 和 version 字段有一些限制:
name 属性就是你的模块名称,下面是一些命名规则:
- name 不能大于 214 个字符。如果是作用域包(前缀名称),也包含它的作用域(如 xxx/xxxmodule)。
 - 作用域包的 name 可以以 
.或者_开头。非作用域包的话是不允许的。 - 新包的 name 中 不能包含大写字母。
 - name 最终会成为 URL/命令行中的参数/文件名的一部分,因此 name 中不能包含 非 URL 安全的字符。
 
下面是官网的一些建议
- 不要使用和 node 核心模块一样的名称。
 - name 中不要含有 
js和node。- It's assumed that it's js, since you're writing a package.json file, and you can specify the engine using the "engines" field. (See below.)
 
 - name 属性会成为模块 url、命令行中的一个参数或者一个文件夹名称,任何非 url 安全的字符在 name 中都不能使用,也不能以 
_或.开头。 - name 属性也许会被写在 
require()的参数中,所以最好取个简短而语义化的值。 - 创建一个模块前可以先到后边的网址查查 name 是否已经被占用. https://www.npmjs.com/
 
**
name 属性可以有一些前缀如 e.g.
@myorg/mypackage
在 npm-scope(7) 的文档中可以看到详细说明
2. version
对于要发布的包来说,version 和 name 一样重要。version 必须能够被 npm 依赖的一个 node-semver 模块解析。具体规则见下面的 dependencies 模块。
3. description
一个描述,方便别人了解你的模块作用。也能够帮助别人使用 npm search 搜索到你的包。
4. keywords
一个字符串数组,和 description 一样能够帮助人们使用 npm search 搜索到你的包。
5. homepage
注意:这个项目
主页url和url属性不同,如果你填写了 url 属性,npm 注册工具会认为你把项目发布到其他地方了,获取模块的时候不会从 npm 官方仓库获取,而是会重定向到 url 属性配置的地址。原文档中用了 spit (吐) 这个单词,作者表示他不是在开玩笑:
链接到项目主页的 URL
"homepage": "https://github.com/owner/project#readme"
6. bugs
链接到项目 issues 的 url 或者 提 issues 的邮箱地址。被你的模块,坑到的人可以通过这里吐槽
url 和 email 可以任意填或不填,如果只填一个,可以直接写成一个字符串而不是对象。如果填写了 url,npm bugs 命令会使用这个 url。
{
  "url": "https://github.com/owner/project/issues",
  "email": "[email protected]"
}
7. license
你应该为你的模块指定一个 license 协议,让其他用户就能够知道你的包可以如何使用,使用该模块有哪些限制。在一些旧的包中 license 是使用对象 or 对象数组声明的,现在已经不推荐使用了。
你可以在 https://spdx.org/licenses/ (opens new window)这个地址查阅协议列表 。
最简单的,例如你用 BSD-3-Clause 或 MIT 之类的协议,如下:
{ 
  "license" : "BSD-3-Clause" 
}
8. author, contributors
author 是一个码农作者, "contributors" 是一个码农贡献者数组,url 和 email 可选,描述用户信息的还有一个 "maintainers"(维护者)属性。
person 是一个有一些描述属性的对象,如下 like this:
{ 
  "name" : "Barney Rubble",
  "email" : "[email protected]",
  "url" : "http://barnyrubble.tumblr.com/"
}
或者可以简写到一个字符串中,npm 会自动解析:
{
  "author": "Barney Rubble <[email protected]> (http://barnyrubble.tumblr.com/)"
}
9. files
files 属性的值是一个数组,内容是模块下文件名或者文件夹名,如果是文件夹名,则文件夹下所有的文件也会被包含进来(除非文件被另一些配置排除了)
你也可以在模块根目录下创建一个 .npmignore 文件(windows 下无法直接创建以"."开头的文件,使用 linux 命令行工具创建如 git bash),写在这个文件里边的文件即便被写在 files 属性 里边也会被排除在外,这个文件的写法 .gitignore 类似。
10. main
如果你的模块是用在客户端上的,那么应该指定
browser字段而不是main字段。
main 属性指定了程序的主入口文件。意思是,如果你的模块被命名为 foo,用户安装了这个模块并通过 require("foo") 来使用这个模块,那么 require 返回的内容就是 main 属性指定的文件中 module.exports 指向的对象。
它应该指向模块根目录下的一个文件。对大对数模块而言,这个属性更多的是让模块有一个主入口文件,然而很多模块并不写这个属性。
11. bin
很多模块有一个或多个需要配置到 PATH 路径下的可执行模块,npm 让这个工作变得十分简单(实际上 npm 本身也是通过 bin 属性安装为一个可执行命令的)
bin字段包含了一组命令到本地文件名的映射。安装包的时候,对于全局安装 npm 会创建一个文件到 prefix/bin 的符号链接,而对于局部安装,npm会创建文件到 ./node_modules/.bin/ 的符号链接。
{
  "bin": {
    "myapp": "./cli.js"
  }
}
模块安装的时候,若是全局安装,则 npm 会为 bin 中配置的文件在 bin 目录下创建一个软连接(对于windows系统,默认会在 C:\Users\username\AppData\Roaming\npm 目录下),若是局部安装, 则会在项目内的 ./node_modules/.bin/ 目录下创建一个软链接。
此,按上面的例子,当我们安装 myapp 的时候,它就会创建一个从 cli.js 到 /usr/local/bin/myapp 的符号链接。
如果你的包中只有一条可执行命令并且命令名称和包名相同,那么就可以直接用字符串来声明:
{
  "name": "my-program",
  "version": "1.2.5",
  "bin": "./path/to/program"
}
12. man
制定一个或通过数组制定一些文件来让 linux 下的 man 命令查找文档地址。
如果只有一个文件被指定的话,安装后直接使用 man + 模块名称,而不管 man 指定的文件的实际名称。例如:
{ 
  "name" : "foo",
  "version" : "1.2.3",
  "description" : "A packaged foo fooer for fooing foos",
  "main" : "foo.js",
  "man" : "./man/doc.1"
}
通过 man foo 命令会得到 ./man/doc.1 文件的内容。
如果 man 文件名称 不是以模块名称开头的,安装的时候会给加上模块名称前缀。因此,下面这段配置:
{ 
  "name" : "foo",
  "version" : "1.2.3",
  "description" : "A packaged foo fooer for fooing foos",
  "main" : "foo.js",
  "man" : [ "./man/foo.1", "./man/bar.1" ]
}
会创建一些文件来作为 man foo 和 man foo-bar 命令的结果。
man 文件必须以数字结尾,或者如果被压缩了,以 .gz 结尾。数字表示文件将被安装到 man 的哪个部分。
{ 
  "name" : "foo",
  "version" : "1.2.3",
  "description" : "A packaged foo fooer for fooing foos",
  "main" : "foo.js",
  "man" : [ "./man/foo.1", "./man/foo.2" ]
}
会创建 man foo 和 man 2 foo 两条命令。
13. directories
CommonJs 通过 directories 来制定一些方法来描述模块的结构,看看 npm 的 package.json 文件 https://registry.npmjs.org/npm/latest (opens new window),可以发现里边有这个字段的内容。
"directories": {
  "bin": "./bin",
  "doc": "./doc",
  "lib": "./lib",
  "man": "./man"
}
目前这个配置没有任何作用,将来可能会整出一些花样来。
(1). directories.lib
告诉用户模块中 lib 目录在哪,这个配置目前没有任何作用,但是对使用模块的人来说是一个很有用的信息。
(2). directories.bin
如果你在这里指定了 bin 目录,这个配置下面的文件会被加入到bin路径下,如果你已经在 package.json 中配置了 bin目录,那么这里的配置将不起任何作用。
(3). directories.man
指定一个目录,目录里边都是 man 文件,这是一种配置 man 文件的语法糖。
(4). directories.doc
在这个目录里边放一些 markdown 文件,可能最终有一天它们会被友好的展现出来(应该是在 npm 的网站上)
(5). directories.example
放一些示例脚本,或许某一天会有用 - -!
14. repository
指定了代码仓库的位置,对想要为你的项目贡献代码的人有帮助。像这样:
"repository" :
  {
    "type" : "git",
    "url" : "https://github.com/npm/npm.git"
  }
"repository" :
  {
    "type" : "svn",
    "url" : "https://v8.googlecode.com/svn/trunk/"
  }
若你的模块放在 GitHub , GitHub gist , Bitbucket , or GitLab 的仓库里, npm install 的时候可以使用缩写标记来完成:
"repository": "npm/npm"
"repository": "gist:11081aaa281"
"repository": "bitbucket:example/repo"
"repository": "gitlab:another/repo"
15. script
scripts 属性是一个对象,里边指定了项目的生命周期个各个环节需要执行的命令。key 是生命周期中的事件,value 是要执行的命令。
具体的内容有 install start stop 等,详见 Scripts (opens new window)。
16. config
用来设置一些项目不怎么变化的项目配置,例如 port 等。
用户用的时候可以使用如下用法:
http.createServer(...).listen(process.env.npm_package_config_port)
可以通过 npm config set foo:port 80 来修改 config。详见 Config。
{ 
  "name" : "foo",
  "config" : { 
    "port" : "8080"
  }
}
17. dependencies
dependencies 属性是一个对象,配置模块依赖的模块列表,key 是模块名称,value 是版本范围,版本范围是一个字符,可以被一个或多个空格分割。
dependencies 也可以被指定为 tarball(压缩包) 或 git URL(Git地址) 标识。
注意:请不要将测试、编译或者其他在开发阶段使用的工具放在
dependencies中。
版本范围说明:详见node-semver:README Versions 部分。
相关阅读:语义化版本号
摘取一部分常见版本范围说明:
| 操作符 | 意义 | 说明 | 
|---|---|---|
version | 
等于(精确匹配版本) | 1.27 将仅且只匹配版本1.27 | 
< | 
小于 | <1.2.7 将匹配版本1.2.6、1.0.1、0.0.2,但不匹配版本1.2.7、2.0.0等 | 
<= | 
小于等于 | <=1.2.7 将匹配版本1.2.7、1.2.6,但不匹配版本1.2.8、2.0.0等 | 
> | 
大于 | >1.2.7 将匹配版本1.2.8、2.0.0,但不匹配版本1.2.7、1.0.0等 | 
>= | 
大于等于 | >=1.2.7 将匹配版本1.2.7、2.0.0,但不匹配版本1.2.6、1.0.0等 | 
~ | 
匹配次要版本 | ~1.2.3:= >=1.2.3 <1.(2+1).0:= >=1.2.3 <1.3.0-0 | 
~ | 
匹配次要版本 | ~1.2:= >=1.2.0 <1.(2+1).0:= >=1.2.0 <1.3.0-0 | 
~ | 
匹配次要版本 | ~1:= >=1.0.0 <(1+1).0.0:= >=1.0.0 <2.0.0-0 | 
^ | 
匹配兼容版本 | ^2.3.1:与2.3.1版本兼容即>=2.3.1 < 3.0.0,不改变大版本号 | 
{ 
  "dependencies" :
  { 
    "foo" : "1.0.0 - 2.9999.9999", // 相当于 >=version1 <=version2
    "bar" : ">=1.0.2 <2.1.2", // 相当于 >= 1.0.2 < 2.1.2 之间的版本
    "baz" : ">1.0.2 <=2.3.4", // 相当于 > 1.0.2 <= 2.3.4 之间的版本
    "boo" : "2.0.1", // 只匹配 2.0.1 版本
    "qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0",
    "asd" : "http://asdf.com/asdf.tar.gz",
    "til" : "~1.2", // 相当于 >= 1.2.0 < 1.3.0-0
    "elf" : "~1.2.3", // 相当于 >= 1.2.3 < 1.3.0-0
    "two" : "2.x", // 仅 2.几 的版本
    "thr" : "3.3.x", // 仅 3.3.几 的版本
    "lat" : "latest",
    "dyl" : "file:../dyl",
  }
}
18. URLs as Dependencies
在版本范围的地方可以写一个 url 指向一个压缩包,模块安装的时候会把这个压缩包下载下来安装到模块本地。
19. Git URLs as Dependencies
commit-ish可以是任意标签,哈希值,或者可以检出的分支,默认是 master 分支。
Git url 可以像下面一样:
git://github.com/user/project.git#commit-ish
git+ssh://user@hostname:project.git#commit-ish
git+ssh://user@hostname/project.git#commit-ish
git+http://user@hostname/project/blah.git#commit-ish
git+https://user@hostname/project/blah.git#commit-ish
20. GitHub URLs
支持 github 的 username/modulename 的写法,# 后边可以加后缀写明分支 hash 或 标签:
{
  "name": "foo",
  "version": "0.0.0",
  "dependencies": {
    "express": "visionmedia/express",
    "mocha": "visionmedia/mocha#4727d357ea"
  }
}
21. Local Paths
npm2.0.0 版本以上可以提供一个本地路径来安装一个本地的模块,通过 npm install xxx --save 来安装,格式如下:
../foo/bar
~/foo/bar
./foo/bar
/foo/bar
package.json 生成的相对路径如下:
{
  "name": "baz",
  "dependencies": {
    "bar": "file:../foo/bar"
  }
}
这种属性在离线开发或者测试需要用 npm install 的情况,又不想自己搞一个 npm server 的时候有用,但是发布模块到公共仓库时不应该使用这种属性。
22. devDependencies
测试、编译或者其他在开发阶段使用的相关工具应该放在
devDependencies中。
如果有人想要下载并使用你的模块,也许他们并不希望或需要下载一些你在开发过程中使用的额外的测试或者文档框架。
在这种情况下,最好的方法是把这些依赖添加到 devDependencies 属性的对象中。
这些模块会在 npm link 或者 npm install 的时候被安装,也可以像其他npm配置一样被管理,详见 npm 的 Config 文档。
对于一些跨平台的构建任务,例如把 CoffeeScript 编译成 JavaScript,就可以通过在 package.json 的 script 属性里边配置 prepublish 脚本来完成这个任务,然后需要依赖的 coffee-script 模块就写在 devDependencies 属性种。
例如:
{ 
  "name": "ethopia-waza",
  "description": "a delightfully fruity coffee varietal",
  "version": "1.2.3",
  "devDependencies": {
    "coffee-script": "~1.6.3"
  },
  "scripts": {
    "prepublish": "coffee -o lib/ -c src/waza.coffee"
  },
  "main": "lib/waza.js"
}
prepublish 脚本会在发布之前运行,因此用户在使用之前就不用再自己去完成编译的过程了。在开发模式下,运行 npm install 也会执行这个脚本(见 npm Scripts 文档),因此可以很方便的调试。
23. peerDependencies
在某些开发场景下,你的包需要某些依赖的支持,但是没必要去安装,因为包的宿主会去安装这些依赖,这时就可以用 peerDependencies 去声明一下需要依赖的包和版本,如果出问题 npm 就会有警告来提醒使用者去解决版本冲突问题。
有时候做一些插件开发,比如 grunt 等工具的插件,它们往往是在 grunt 的某个版本的基础上开发的,而在他们的代码中并不会出现 require("grunt") 这样的依赖,dependencies 配置里边也不会写上 grunt 的依赖,为了说明此模块只能作为插件跑在宿主的某个版本范围下,可以配置 peerDependencies:
{
  "name": "tea-latte",
  "version": "1.3.5",
  "peerDependencies": {
    "tea": "2.x"
  }
}
上面这个配置确保再 npm install 的时候 tea-latte 会和 2.x 版本的 tea 一起安装,而且它们两个的依赖关系是同级的:
├── [email protected]
└── [email protected]
上面这个配置的目的是让 npm 知道,如果要使用此插件模块,请确保安装了兼容版本的宿主模块。
24. bundledDependencies
上面的单词少个 d,写成 bundleDependencies 也可以。指定发布的时候会被一起打包的模块。
25. optionalDependencies
如果一个依赖模块可以被使用, 同时你也希望在该模块找不到或无法获取时 npm 继续运行,你可以把这个模块依赖放到 optionalDependencies 配置中。这个配置的写法和 dependencies 的写法一样,不同的是这里边写的模块安装失败不会导致 npm install 失败。
当然,这种模块就需要你自己在代码中处理模块确实的情况了,例如:
try {
  var foo = require('foo')
  var fooVersion = require('foo/package.json').version
} catch (er) {
  foo = null
}
if ( notGoodFooVersion(fooVersion) ) {
  foo = null
}
// .. then later in your program ..
if (foo) {
  foo.doFooThings()
}
optionalDependencies 中的配置会覆盖 dependencies 中的配置,最好只在一个地方写。
26. engines
你可以通过 engines 来指定项目运行的 node 版本范围,如下:
{
  "engines": {
    "node": ">=0.10.3 <15",
  }
}
和 dependencies 一样,如果你不指定版本范围或者指定为 *,任何版本的 node 都可以。也可以指定一些 npm 版本可以正确的安装你的模块,例如:
{
  "engines": {
    "npm": "~1.0.20"
  }
}
要注意的是,除非你设置了 engine-strict 属性,engines 属性是仅供参考的。
27. engineStrict
注意:这个属性已经弃用,将在 npm 3.0.0 版本干掉。
28. os
可以用来指定你的程序运行的操作系统:
{
  "os": [
    "darwin",
    "linux"
  ]
}
也可以指定黑名单而不是白名单:
{
  "os" : [ 
    "!win32"
  ]
}
服务的操作系统是由 process.platform 来判断的,这个属性允许黑白名单同时存在,虽然没啥必要这样搞...
29. cpu
限制模块只能在某某 cpu 架构下运行
{
  "cpu": [
    "x64",
    "ia32"
  ]
}
同样可以设置黑名单:同上
cpu 架构通过 process.arch 判断
30. preferGlobal
如果您的软件包主要用于安装到全局的命令行应用程序,那么该值设置为 true ,如果它被安装在本地,则提供一个警告。实际上该配置并没有阻止用户把模块安装到本地,只是防止该模块被错误的使用引起一些问题。
31. private
如果设置了private: true,那么 npm 就不会发布你的包。这是一种防止意外发布私有库的方法。如果要确保给定的包只发布到特定的库中,那么可以使用 publishConfig 发布时重写配置参数。
32. publishConfig
这个配置是会在模块发布时用到的一些值的集合。如果你不想模块被默认被标记为最新的,或者默认发布到公共仓库,可以在这里配置 tag 或仓库地址。详细配置请看 config。
33. DEFAULT VALUES
npm 设置了一些默认参数,如:
{
  "scripts": {
	  "start": "node server.js"
  }
}
如果模块根目录下有一个 server.js 文件,那么 npm start 会默认运行这个文件。
{
  "scripts":{
    "preinstall": "node-gyp rebuild"
  }
}
如果模块根目录下有 binding.gyp, npm 将默认用 node-gyp 来编译 preinstall 的脚本。
{
  "contributors": [...]
}
若模块根目录下有 AUTHORS 文件,则 npm 会按 Name (url) 格式解析每一行的数据添加到 contributors 中,可以用 # 添加行注释
三. 总结
遇到不懂的还是要多看多总结,官方文档是个好东西不要因为全是英文的就不看啊