(譯)package.json詳解
概述
本文囊括了所有package.json文件中你需要知道的細節。注意package.json必須是純JSON的,而不僅僅是一個JavaScript對象字面量。
該文件描述的很多行為都受npm-config中的配置影響。
下面分別介紹package.json中各個字段的含義和用法。
name
name和version字段是package.json文件中最重要的字段。這是必須的字段,如果你的npm包沒有指定這兩個字段,將無法被安裝。name和version字段被假定組合成一個唯一的標識符。包內容的更改和包版本的更改是同步的。
name字段的含義不需要過多解釋,就是npm包名。
幾個規則:
1. name的長度必須小於等於214個字符。
2. name不能以"."(點)或者"_"(下划線)開頭。
3. name中不能包含大寫字母。
4. name最終將被用作URL的一部分、命令行的參數和文件夾名。因此,name不能含有非URL安全的字符。
幾個建議:
1. 不要使用已存在的name作為包名。
2. 不要在name中使用"js"和"node",這會假定這是js文件,一旦你寫一個package.json文件,你就可以在"engines"字段中指定解釋器引擎。
3. name字段可能會被作為傳輸傳遞給require()函數,因此它最好是簡短的、自描述的。
4. 你可能會需要在深入開發一個包之前先檢查npm的registry來確認某個name是否被使用過,可以參考https://www.npmjs.com/。
一個name可以用scope來指定一個前綴,比如@myorg/mypackage,可以參考npm-scope。
version
name和version字段是package.json文件中最重要的字段。這是必須的字段,如果你的npm包沒有指定這兩個字段,將無法被安裝。name和version字段被假定組合成一個唯一的標識符。包內容的更改和包版本的更改是同步的。
version字段必須能夠被node-semver解析,node-semver作為依賴項被捆綁進了npm中。(可以使用npm install semver來使用)
關於版本號和范圍的信息可以參考semver。
description
npm包的描述,description是一個字符串。它可以幫助人們在使用npm search時找到這個包。
keywords
npm包的關鍵字,keywords是一個字符串的數組。它可以幫助人們在使用npm search時找到這個包。
homepage
項目主頁的url。
注意: 這和"url"不一樣。如果你放一個"url"字段,registry會以為是一個跳轉到你發布在其他地方的地址,然后鄙視你。
bugs
改項目的issue跟蹤頁面或這報告issue的email地址。這對使用這個包遇到問題的用戶會有幫助。
差不多是這樣:
{
"url" : "https://github.com/owner/project/issues",
"email" : "project@hostname.com"
}
你可以擇其一或者兩個都寫上。如果只想提供一個url,你可以對"bugs"字段指定一個字符串而不是object。
如果提供了一個url,它會被用於npm bugs命令。
license
你應該對你的包指定一個license來讓用戶知道他們的使用權利和和任何限制。
如果你正在使用BSD-2-Clause或MIT這樣的通用許可證,可以為你的license添加一個當前SPDX的許可證標識符,比如:
{ "license" : "BSD-3-Clause" }
你可以查看SPDX許可證標識符的完整列表,理想情況下你應該挑選一個經過OSI核准的標識符。
如果你的包在多個通用許可證下被授權,使用一個SPDX許可證表達式語法v2.0,比如:
{ "license" : "(ISC OR GPL-3.0)" }
如果你正在使用的許可未被授予一個SPDX標識符,或者你正在使用自定義的許可證,使用如下:
{ "license" : "SEE LICENSE IN <filename>" }
然后在包的根目錄下提供一個叫<filename>的許可證文件。
一些舊的包使用license對象或一個"license"屬性包含一個license的數組:
// Not valid metadata
{ "license" :
{
"type" : "ISC",
"url" : "http://opensource.org/licenses/ISC"
}
}
//Notvalidmetadata
{
"licenses": [
{
"type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.php"
},
{
"type": "Apache-2.0",
"url": "http://opensource.org/licenses/apache2.0.php"
}
]
}
上述這種風格的寫法已經被廢棄了,取而代之的是SPDX表達式:
{ "license": "ISC" }
{ "license": "(MIT OR Apache-2.0)" }
最后,如果你不希望授權別人以任何形式使用私有包或未發布的包,可以這樣寫:
{ "license": "UNLICENSED"}
也可以設置:
"private": true
來防止意外的發布。
關於人的字段: author, contributors
author是一個人,contributors是一些人的數組。person是一個對象,擁有必須的name字段和可選的url和email字段,像這樣:
{
"name": "Barney Rubble",
"email": "b@rubble.com",
"url": "http://barnyrubble.tumblr.com/"
}
或者你也可以使用單個字符串的精簡形式,npm會幫你解析它:
"Barney Rubble <b@rubble.com> (http://barnyrubble.tumblr.com/)"
這里email和url也是可選的。
npm也會使用你提供的npm用戶信息來設置一個頂級的"maintainers"字段。
files
files字段是一個被項目包含的文件名數組,如果你在里面放一個文件夾名,那么這個文件夾中的所有文件都會被包含進項目中(除非是那些在其他規則中被忽略的文件)。
你還可以在包的根目錄或子目錄下提供一個".npmignore"文件來忽略項目包含文件,即使這些文件被包含在files字段中。.npmignore文件和.gitignore的功能很像。
某些文件總是被包含的,不論是否在規則中指定了它們:
package.json
README (and its variants)
CHANGELOG (and its variants)
LICENSE / LICENCE
相反地,一些文件總是被忽略:
.git
CVS
.svn
.hg
.lock-wscript
.wafpickle-N
*.swp
.DS_Store
._*
npm-debug.log
main
main字段指定了模塊的入口程序文件。就是說,如果你的模塊名叫"foo",用戶安裝了它,並且調用了 require("foo"),則這個main字段指定的模塊的導出對象會被返回。
這應該是一個相對於包根目錄的模塊標識。
對於大部分模塊來說,main字段除了指定一個主入口文件以外沒什么其他用處了。
bin
許多包有一個或多個可執行文件希望被安裝到系統路徑。在npm下要這么做非常容易(事實上,npm就是這么運行的)。
這需要在你的package.json中提供一個bin字段,它是一個命令名和本地文件名的映射。在安裝時,如果是全局安裝,npm將會使用符號鏈接把這些文件鏈接到prefix/bin,如果是本地安裝,會鏈接到./node_modules/.bin/。
比如,要使用myapp作為命令時可以這么做:
{ "bin" : { "myapp" : "./cli.js" } }
這么一來,當你安裝myapp,npm會從cli.js文件創建一個到/usr/local/bin/myapp的符號鏈接(這使你可以直接在命令行執行myapp)。
如果你只有一個可執行文件,那么它的名字應該和包名相同,此時只需要提供這個文件路徑(字符串),比如:
{
"name": "my-program",
"version": "1.2.5",
"bin": "./path/to/program"
}
這和以下這種寫法相同:
{
"name": "my-program",
"version": "1.2.5",
"bin": {
"my-program": "./path/to/program"
}
}
man
指定一個單一的文件名或一個文件名數組來讓man程序使用。
如果只給man字段提供一個文件,則安裝完畢后,它就是man <pkgname>的結果,這和此文件名無關,比如:
{
"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這個文件。
如果指定的文件名並未以包名開頭,那么它會被冠以前綴,像這樣:
{
"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后綴(當它被壓縮時)。這個數字說明了這個文件被安裝到哪個節中。
{
"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而創建。
directories
CommonJS Packages規范說明了幾種你可以用directories對象來標示你的包結構的方法。如果你去看npm's package.json,你會看到它標示出出doc、lib和man。
在未來,這個信息可能會被用到。
directories.lib
告訴你庫文件夾的位置,目前沒有什么地方需要用到lib文件夾,但是這是重要的元信息。
directories.bin
如果你在directories.bin中指定一個bin目錄,在這個目錄中的所有文件都會被當做在bin來使用。
由於bin指令的工作方式,同時指定一個bin路徑和設置directories.bin將是一個錯誤。如果你想指定獨立的文件,使用bin,如果想執行某個文件夾里的所有文件,使用directories.bin。
directories.man
directories.man指定的文件夾里都是man文件,系統通過遍歷這個文件夾來生成一個man的數組。
directories.doc
把markdown文件放在這。也許某一天這些文件將被漂亮地展示出來,不過這僅僅是也許。
directories.example
把示例腳本放在這。也許某一天會被用到。
repository
指明你的代碼被托管在何處,這對那些想要參與到這個項目中的人來說很有幫助。如果git倉庫在github上,用npm docs命令將會找到你。
像這樣:
{
"repository": {
"type": "git",
"url": "https://github.com/npm/npm.git"
}
}
url應該是公開且可用的(可能是只讀的),這個url應該可以被版本控制系統不經修改地處理。不應該是一個在瀏覽器中打開的html項目頁面,這個url是給計算機使用的。
對於github、github gist、Bitbucket或GitLab的倉庫,你可以在npm install中使用相同的縮寫形式:
"repository": "npm/npm"
"repository": "gist:11081aaa281"
"repository": "bitbucket:example/repo"
"repository": "gitlab:another/repo"
scripts
scripts字段是一個由腳本命令組成的字典,這些命令運行在包的各個生命周期中。這里的鍵是生命周期事件名,值是要運行的命令。可以參考npm-scripts獲取配置scripts的更多信息。
config
config字段是一個對象,可以用來配置包腳本中的跨版本參數,比如如下這個實例:
{
"name": "foo",
"config": {
"port": "8080"
}
}
然后有一個start命令引用npm_package_config_port環境變量,用戶也可以用如下方式改寫:
npm config set foo:port 8001
可以參考npm-config和npm-scripts獲得更多關於包配置的信息。
dependencies
dependencies字段是一個對象,它指定了依賴的包名和其版本范圍的映射。版本范圍是個有一個或多個空白分隔描述符的字符串。dependencies字段還可以用tarball或者git URL。
請不要將測試或過渡性的依賴放到dependencies中,請參考下面的devDependencies。
可以參考semver獲取更多關於指定版本范圍的細節信息。
1. version 必須確切匹配這個version
2. \>version 必須大於這個version
3. \>=version 必須大於等於這個version
4. < version 必須小於這個version
5. <=version 必須小於等於這個version
6. ~version 大約相當於version,參考[semver](https://docs.npmjs.com/misc/semver)
7. ^version 與version兼容,參考[semver](https://docs.npmjs.com/misc/semver)
8. 1.2.x 可以是1.2.0、1.2.1等,但不能是1.3.0
9. http://... 參考下面的URL作為依賴項
10. \* 匹配任何版本
11. ""(空字符串) 匹配任何版本,和\*一樣
12. version1 - version2 相當於 >=version1 <=version2
13. range1 || range2 range1或range2其中一個滿足時采用該version
14. git... 參考下面的Git URL作為依賴項
15. user/repo 參考下面的GitHub URLs
16. tag 一個以tag發布的指定版本,參考[npm-tag](https://docs.npmjs.com/cli/tag)
17. path/path/path 參考下面的本地Paths
舉個栗子,下面這種寫法是合法的:
{
"dependencies": {
"foo": "1.0.0 - 2.9999.9999",
"bar": ">=1.0.2 <2.1.2",
"baz": ">1.0.2 <=2.3.4",
"boo": "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",
"elf": "~1.2.3",
"two": "2.x",
"thr": "3.3.x",
"lat": "latest",
"dyl": "file:../dyl"
}
}
URLs作為依賴項
可以在version上指定一個壓縮包的url。
當執行npm install時這個壓縮包會被下載並且安裝到本地。
Git URLs作為依賴項
Git URLs可以是如下幾種形式之一:
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
commit-ish可以是任何tag、sha或者branch,並作為一個參數提供給git進行checkout,默認值是master。
GitHub URLs
從1.1.65版本開始,你可以引用Github urls作為版本號,比如"foo": "user/foo-project"。也可以包含一個commit-ish后綴,舉個栗子:
{
"name": "foo",
"version": "0.0.0",
"dependencies": {
"express": "visionmedia/express",
"mocha": "visionmedia/mocha#4727d357ea"
}
}
本地路徑
從版本2.0.0開始你可以提供一個包的本地路徑。本地路徑可以在你使用npm install -S或npm install --save時被保存,具體形式如下:
../foo/bar
~/foo/bar
./foo/bar
/foo/bar
在下面這種情況下它會被規范化成為一個相對路徑並且加入到你的package.json文件中,比如:
{
"name": "baz",
"dependencies": {
"bar": "file:../foo/bar"
}
}
這個特性有助於當你不想從一個外部服務器安裝npm包的情況,比如本地離線開發和創建測試,但最好不要在發布包到公共registry時這樣使用。
devDependencies
如果有人計划在他們的項目中下載和使用你的模塊,但他們可能並不想或並不需要你開發所使用的外部測試和文檔框架。
在這種情況下,最好將這些附加的項放在devDependencies中。
這些項將會在根目錄下執行npm link或npm install時被安裝,並且可以像其他npm配置參數一樣被管理。可以參考npm-config獲得更多信息。
對於那些非特定平台的構建步驟,比如編譯CoffeeScript或把其他語言轉換成JavaScript,可以使用prepublish腳本來處理,並且把這個過程的依賴包放在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腳本會在publishing前運行,這樣用戶就可以不用自己去require來編譯就能使用。在開發模式下(比如本地運行npm install),將會執行這個腳本,這樣測試就非常方便了。
peerDependencies
在某些情況下,當一個主機無法require依賴包時,你會想要告訴它還有哪些工具或庫與這個依賴包兼容。這通常被成為一個插件。尤其是在host文檔中聲明的模塊會暴露一個特定的接口。
舉個栗子:
{
"name": "tea-latte",
"version": "1.3.5",
"peerDependencies": {
"tea": "2.x"
}
}
這將確保tea-latte這個包只會和2.x版本的tea一起被安裝。執行npm install tea-latte可能產生以下關系圖:
├── tea-latte@1.3.5
└── tea@2.2.0
注意:如果沒有在依賴樹中顯式聲明比它們更高的依賴版本,版本1和版本2的npm將會自動安裝peerDependencies。在npm的下一個大版本npm3中,情況將完全不同。你將收到一個警告,告訴你peerDependency還沒有被安裝。在npm1和npm2中這個行為經常會導致混亂,新的npm版本的設計將會極力避免這種情況。
試圖安裝一個有沖突的依賴項的插件將會導致一個錯誤。因此你必須確保你的插件的依賴項版本范圍盡可能大,並且不要把版本鎖死在一個特點的補丁版本上。
假設主機使用semver進行編譯,只改變這個包的主版本將會導致你的插件不可用。因此,如果你的插件的某個依賴包運行在每個1.x版本下,使用"^1.0"或"1.x"。如果你需要的功能在1.5.2版本中,使用">= 1.5.2 < 2"。
bundledDependencies
在發布包時,包名的數組會被打包進去。
如果拼寫成"bundleDependencies"(少個d),也是可以的。
optionalDependencies
如果一個依賴項可用,但希望在這個依賴項無法被找到或者安裝時失敗npm還能繼續處理(不中斷),那么你可以把它放在optionalDependencies中。和dependencies一樣,optionalDependencies是一個包名和版本號或url的映射。區別在於optionalDependencies中的依賴構建失敗時不會導致npm整體安裝失敗。
但是你的程序依然有責任處理這種缺失的依賴項,比如這樣:
try{
varfoo=require('foo')varfooVersion=require('foo/package.json').version
}catch(er){
foo=null
}if(notGoodFooVersion(fooVersion)){
foo=null
}//..thenlaterinyourprogram..if(foo){
foo.doFooThings()
}
optionalDependencies中的項會覆蓋dependencies中的同名項,所以一個特定名字的項最好只出現在一個地方。
engines
你可以指定node的工作版本:
{ "engines" : { "node" : ">=0.10.3 <0.12" } }
和dependencies類似,如果你不指定一個node版本(或者你用'*'指定),則任何一個node版本都可以。
如果你指定了一個'engines'字段,則npm將會在某處包含這個node版本。如果忽略'engines'字段,則npm只會僅僅假設這個包工作在node下。
你還可以使用'engines'字段來指定可以安裝這個包的npm版本,舉個栗子:
{ "engines" : { "npm" : "~1.0.20" } }
請注意,除非用戶設置了engine-strict標記,否則這個字段只是一個建議值。
engineStrict
這個特性在npm 3.0.0中已經廢棄。
npm 3.0.0之前的版本,這個特性用來處理那些設置了engine-strict標記的包。
os
可以指定模塊運行的操作系統:
"os" : [ "darwin", "linux" ]
也可以使用操作系統黑名單來替代白名單,只要在前面加個'!':
"os" : [ "!win32" ]
主機的操作系統可以通過process.platform來確定。
雖然找不到什么很好的理由支持這么做,但是這里還可以黑名單和白名單混用。
cpu
如果你的代碼只能運行在特定的cpu架構上,你可以指明:
"cpu" : [ "x64", "ia32" ]
和os選項類似,你還可以使用黑名單:
"cpu" : [ "!arm", "!mips" ]
主機的cpu架構可以通過process.arch來確定。
preferGlobal
如果你的包是一個需要進行全局安裝的命令行應用,需要設置preferGlobal為true,如果這個包被本地安裝會報出一個警告。
這個選項並不會阻止用戶本地安裝這個包,但這么做確實能在包未按照預期被安裝造成諸多麻煩時提供一些提示。
private
如果你在包的package.json中設置"private": true,則npm會拒絕發布它。
這是防止私有包被以外發布的一種方法。如果你希望包裝某個包只能被發布到特定的一個registry中(比如,一個內部的registry),則可以使用下面的publishConfig字典來描述以在publish-time重寫registry配置參數。
publishConfig
這是一個在publish-time時會用到的配置集合。當你想設置tag、registry或access時特別有用,所以你可以確保一個給定的包無法在沒有被打上"latest"標記時就被發布到全局公共的registry。
任何配置都可以被覆蓋,當然可能只有"tag", "registry"和"access"和發布意圖有關。
參考npm-config來查看那些可以被覆蓋的配置項列表。
DEFAULT VALUES
npm會基於包內容設置一些默認值。
"scripts": {"start": "node server.js"}
如果包的根目錄中有一個server.js,那么npm會用它來作為入口文件:運行node server.js。
"scripts":{"preinstall": "node-gyp rebuild"}
如果包的根目錄中有一個binding.gyp文件,那么npm會在運行preinstall命令編譯時使用它。
"contributors": [...]
如果包的根目錄中有一個AUTHORS文件,那么npm會把它的每一個行格式化成Name \< email \> (url)的形式,其中email和url是可選的。以一個#或者空白符開頭的行將被忽略。
參考資料
semver
npm-init
npm-version
npm-config
npm-config
npm-help
npm-faq
npm-install
npm-publish
npm-uninstall