npm的scripts命令可以做其它構(gòu)建工具所用的一切事情,而且它更加簡(jiǎn)明、更加優(yōu)雅、更少的包依賴和更少的維護(hù)成本。
npm Script
首先,我需要講一下npm是如何管理我們的構(gòu)建腳本的。我們知道npm的構(gòu)建腳本有npm run-script這個(gè)命令(npm run是它的簡(jiǎn)寫(xiě)),npm run的第一個(gè)參數(shù)會(huì)關(guān)聯(lián)到scripts對(duì)象的一個(gè)屬性,它會(huì)將這個(gè)屬性的值做為一個(gè)命令在操作系統(tǒng)默認(rèn)的shell中執(zhí)行。例如,下面的這個(gè)package.json:
{
"name": "myproject",
"devDependencies": {
"jshint": "latest",
"browserify": "latest",
"mocha": "latest"
},
"scripts": {
"lint": "jshint **.js",
"test": "mocha test/"
}
}
如果運(yùn)行npm run lint,npm將開(kāi)啟一個(gè)shell并執(zhí)行jshint **.js,如果運(yùn)行npm run test,npm將開(kāi)啟一個(gè)shell并執(zhí)行mocha test/。而這個(gè)shell環(huán)境已經(jīng)將你的node_modules/.bin文件夾添加到了PATH中,這意味著你安裝的任何依賴可以被直接執(zhí)行--換句話說(shuō),你沒(méi)有必要像./node_modules/.bin/jshint **.js或者$(npm bin)/jshint **.js這樣來(lái)指定它的路徑。
如果你在運(yùn)行npm run時(shí)沒(méi)有寫(xiě)任何參數(shù),那么npm將會(huì)把已經(jīng)有的所有命令列出來(lái),如:
Available scripts in the user-service package:
lint
jshint **.js
test
mocha test/
Shortcut script
為了方便使用,npm也提供了一些命令的快捷使用方式。如:npm test、npm start、npm stop命令等,它們可以省去run。
Pre and Post Hooks
別外一個(gè)很酷的特性是,任何npm的script可以設(shè)置多個(gè)pre-和post-鉤子。例如,如果你執(zhí)行npm run lint,盡管npm預(yù)先并不知道lint任務(wù)是什么,但它會(huì)立即運(yùn)行npm run prelint,然后再執(zhí)行npm run lint,最后再執(zhí)行npm run postlint。pre和postscript也是exit-code-sensitive的,這意味著,如果你的pretestscript以一個(gè)非零退出碼退出,那么NPM先會(huì)立即停止,并且也不會(huì)執(zhí)行再test和posttestscripts。
你不在一個(gè)已經(jīng)是pre-的script上再加一個(gè)pre-,例如,prepretest將會(huì)被忽略。
你也可以在npm中對(duì)一些內(nèi)部的命令使用pre-和post-,如:install、uninstall、publish和update。你不能覆蓋任何內(nèi)部命令的行為,但是你可以通過(guò)pre-和post-來(lái)影響他們的行為。這意味著,你可以做像這樣一件很酷的事情:
"scripts": {
"lint": "jshint **.js",
"build": "browserify index.js > myproject.min.js",
"test": "mocha test/",
"prepublish": "npm run build # also runs npm run prebuild",
"prebuild": "npm run test # also runs npm run pretest",
"pretest": "npm run lint"
}
Passing Arguments
在npm中,另一個(gè)很酷的特性是:傳遞參數(shù)集合。如下:
"scripts": {
"test": "mocha test/",
"test:xunit": "npm run test -- --reporter xunit"
}
通過(guò)這個(gè)配制,我們可以簡(jiǎn)單運(yùn)行npm run test,它將會(huì)執(zhí)行mocha test/,但是我們可以通過(guò)--前輟來(lái)擴(kuò)展它的參數(shù)。
例如,npm run test --anothertest.js將會(huì)執(zhí)行mocha test/ anothertest.js,或者更有用的如npm run test -- --grep parser將會(huì)?解析為mocha test/ --grep parser(它只會(huì)執(zhí)行名為parser的測(cè)試)。
NPM Config Variables
最后一件值得我們注意的事是,npm的package.json中有一個(gè)config命令,它使得任意的一系列值可以被包裝成為一個(gè)在scripts中的環(huán)境變量。例如:
"name": "fooproject",
"config": {
"reporter": "xunit"
},
"scripts": {
"test": "mocha test/ --reporter $npm_package_config_reporter",
"test:dev": "npm run test --fooproject:reporter=spec"
}
這里,config中有一個(gè)reporter屬性,它的值為xunit。這里所有config的屬性都暴露給了環(huán)境變量,你可以通過(guò)加npm_package_config_這個(gè)前輟來(lái)訪問(wèn)它們。
在上面這個(gè)例子中,npm run test命令使用$npm_package_config_reporter變量來(lái)獲取config中reporter的值。
運(yùn)行多個(gè)任務(wù)
像Grunt和Culp這樣的構(gòu)建工具都具有將多個(gè)任務(wù)整合成一個(gè)任務(wù)的能力。而使用npm時(shí)你有兩種方式可以選擇,這取決于哪種語(yǔ)義更加附合你使用場(chǎng)景。如果你的任務(wù)是一個(gè)先決條件(例如,將js最小化之前我們要將所有的js代碼導(dǎo)到一個(gè)文件中),那么使用pre-和post-鉤子會(huì)是更好的選擇。另外你還可以像下面這樣使用bash的&&操作符:
"scripts": {
"build": "npm run build:css && npm run build:js"
}
讓我們來(lái)看下來(lái)這個(gè)例子:
我們的package.json的scripts代碼如下:
"scripts": {
"jslint": "node jslint.js",
"csslint": "node csslint.js",
"build:css": "node build-css.js",
"build:js": "node build-js.js",
"build": "npm run build:css && npm run build:js",
"prebuild:js": "npm run jslint",
"prebuild:css": "npm run csslint"
}
下面是幾個(gè)文件,只是為了在執(zhí)行它們時(shí)打印出信息,代碼如下:
jslint.js
console.log("jslint task running...");
csslint.js
console.log("csslint task running...");
build-css.js
console.log("build:css task running...");
build-js.js
console.log("build:js task running...");
當(dāng)我們執(zhí)行npm run build時(shí),程序執(zhí)行順序如下:
> npm run build:css && npm run build:js
> npm run csslint
> node csslint.js
csslint task running...
> node build-css.js
build:css task running...
> npm run jslint
> node jslint.js
jslint task running...
> node build-js.js
build:js task running...