egghead
How to Use npm Scripts as Your Build Tool
https://egghead.io/lessons/tools-use-a-shorthand-syntax-for-running-multiple-npm-scripts-with-npm-run-all
1. 建立基本的 package.json 檔案
輸入 npm init
接著輸入基本資料
若你不想要輸入資料可以透過下列的參數
npm init --yes
npm init --force
npm init -y
npm init -f
以上方式都是同等效用
2. 執行基本的指令 / test 與 start
{
"name" : "practice" ,
"version" : "1.0.0" ,
"description" : "" ,
"main" : "index.js" ,
"scripts" : {
"test" : "echo \"Error: no test specified\" && exit 1"
},
"keywords" : [],
"author" : "James Yang" ,
"license" : "MIT"
}
修改 scripts 的部份加入測試項目
例如:"test" : "mocha spec/ —require babel-register"
執行測試 npm test
或 npm t
修改 scripts 添加 start
例如:"start":"node index.js"
啟動項目 npm start
3. 建立自訂的指令
除了基本的 start 與 test 你也可以撰寫自訂的指令
但此之前,如何透過環境變數,讓 npm 知道你的檔案位置在哪,然後透過指令的方式執行它
可以使用 npm bin 找出本地端的 node_module 執行檔的資料夾,然後用在 sciprt 指令中
例如:
"eslint" : " $( npm bin) /eslint --cache --fix ./"
若要啟動,執行 npm run eslint
或 npm run-script eslint
即可啟動
你可以透過 npm run env
,列出相關的環境設定
透過 npm run env | grep "^PATH"
找出 PATH 的設定
列出來之後會發現 /Users/james/Projects/James/practice/node_modules/.bin
有列在 PATH 中
所以不需要再 script 上面加 $(npm bin) ,直接執行 eslint 即可
4. 執行串連指令
如果你想要透過一個指令,執行數個指令的話可透過 &&
將指令串接在一起
例如:
{
"scripts" : {
"test" : "mocha tests/node-test.js" ,
"start" : "node index.js" ,
"eslint" : "$(npm bin)/eslint --cache --fix ./" ,
"serial" : "npm test && npm start && npm run eslint"
}
}
執行:npm run serail
5. 執行並連指令
直接把 &&
改成 &
即可
{
"scripts" : {
"test" : "mocha tests/node-test.js --watch & wait" ,
"start" : "node index.js" ,
"eslint" : "$(npm bin)/eslint --cache --fix ./" ,
"serial" : "npm test && npm start && npm run eslint" ,
"parallel" : "npm test & npm start & npm run eslint"
}
}
執行 : npm parallel
在 test
的部分,加上了 --watch
指令,此時 mocha
會監聽 tests/node-test.js
檔案,若有異動就會重新執行。
如果這時候你用 ^C (control+C)將程序關閉的時候, --watch
並不會停止, 可以使用 ps -xa | grep mocha
找出正在監聽的程序 然後用 kill -9 id
將正在監聽的程序終止。為了避免這個狀況發生,就必須在後面加上 wait
指令。確保在中斷的時候停止監聽。
6. 透過 npm-run-all 取代原本的指令
先安裝 npm-run-all
-D
同等於 --save-dev
i
同等於 install
接下來可以把 script 中 npm 與 && 的部分取代掉,直接用 npm-run-all 執行並聯程序。
若要併發的話可以加上 --parallel
參數取代
取代並聯方式
修改前
{
"scripts" : {
"test" : "mocha tests/node-test.js --watch & wait" ,
"start" : "node index.js" ,
"eslint" : "$(npm bin)/eslint --cache --fix ./" ,
"serial" : "npm test && npm start && npm run eslint" ,
"parallel" : "npm test & npm start & npm run eslint"
}
}
使用 npm-run-all
套件不需要使用 wait 指令
修改後
{
"scripts" : {
"test" : "mocha tests/node-test.js --watch" ,
"start" : "node index.js" ,
"eslint" : "$(npm bin)/eslint --cache --fix ./" ,
"serial" : "npm-run-all test start eslint" ,
"parallel" : "npm-run-all test start eslint"
}
}
7. 透過配通符號(wildcard) 執行相似的指令碼
當我們有好幾個 script 名稱格式相似,可以透過配通符號執行
例如:
lint:js
lint:css
lint:css:fix
可以使用 lint:* 或 lint:** 執行所有相似的 script
具體的作法如下:
{
"scripts": {
"test": "mocha tests/node-test.js --watch",
"start": "node index.js",
"eslint": "$(npm bin)/eslint --cache --fix ./",
"serial": "npm-run-all test start eslint",
"parallel" : "npm-run-all test start eslint",
}
}
8. 使用事前執行與事後執行的觸發事件
當執行 script 的時候,可以透過命名的規則,在執行前或執行後添加執行的 script
只要根據執行指令的名稱加上 pre 或 post 即可
例如: customScript
事前執行的 script 取名為 precustomScript
事後執行的 script 取名為 pastcustomScript
即可
然後調用 npm run customScript
就會依序執行
處理 stylefmt 的問題
執行 lint:css:fix 時發現問題
原來是因為 stylefmt 第五版之後就把參數 -R 改為 -r 了
另外延伸的問題是 怎麼指定多個目錄
方法如下
stylefmt -r src/*.css scss/*.scss
stylefmt -r {src/*.css,scss/*.scss}
上面兩個方式等效
此語法稱之為 glob patten
完成的範例:
{
"name" : "practice" ,
"version" : "1.0.0" ,
"description" : "" ,
"main" : "index.js" ,
"scripts" : {
"precustomScript" : "echo 'precustomScript running'" ,
"postcustomScript" : "echo 'postcustomScript running'" ,
"mocha" : "mocha tests/node-test.js --watch" ,
"customScript" : "echo 'npm run customScript'" ,
"test" : "npm-run-all lint mocha" ,
"start" : "node index.js" ,
"eslint" : "$(npm bin)/eslint --cache --fix ./" ,
"serial" : "npm-run-all test start eslint" ,
"parallel" : "npm-run-all --parallel test start eslint" ,
"lint" : "npm-run-all lint:**" ,
"lint:js" : "eslint --cache --fix ./" ,
"lint:css" : "stylelint '**/*.scss' --syntax scss" ,
"lint:css:fix" : "stylefmt -r 'src/*.css, scss/*.scss'"
},
"keywords" : [
"npm" ,
"script" ,
"practice"
],
"author" : "James Yang" ,
"license" : "MIT" ,
"devDependencies" : {
"eslint" : "^3.14.1" ,
"npm-run-all" : "^4.0.1" ,
"stylefmt" : "^5.1.1" ,
"stylelint" : "^7.8.0"
}
}
9. 丟參數給 npm scripts
透過兩個 --
放在 npm 的指令與參數中間
例如:
{
"watch:test" : "npm test -- --watch"
}
10. 透過指令將不同的 script 串連起來
綜合先前的教學,將不同程序的 script 串聯在一起
範例:
{
"build" : "npm-run-all build:*" ,
"prebuild" : "rm -rf public/" ,
"build:html" : "pug --obj data.json src/index.pug --out public/" ,
"build:css" : "node-sass src/index.scss | postcss -c .postcssrc.json | cssmin > public/index.min.css" ,
"build:js" : "mustache data.json src/index.mustache.js | uglifyjs > public/index.min.js"
}
11. 透過 onchange 當程式改變時執行 npm script 指令
範例:當執行 npm run watch
時會同時執行 watch:test 與 watch:lint
需安裝 https://www.npmjs.com/package/onchange 執行 npm i onchange -D
即可透過 script 指令 onchange 監聽檔案改變事件,觸發執行
{
"watch" : "npm-run-all --parallel watch:*" ,
"watch:test" : "npm t -- --watch" ,
"watch:lint" : "onchange '**/*.js' '**/*.scss' -- npm run lint"
}
12. 在 npm script 中使用 package.json 變數
透過 npm run env
指令,可以列出所有 package.json 中可以參考的變數
再透過 grep
取出需要項目
npm run env | grep "npm_package" | less
然後可以看到有個變數稱為 npm_package_version
可以直接套用在 package.json
中 前面加上 字符號
像這樣:
{
"precustomScript" : "echo 'precustomScript running' $npm_package_version"
}
要注意要放在雙引號裡頭,單引號外面才會是變數值,不然會被當成字串使用。
13. 在 package.json 中添加 config 作為參考
例如:
"config" : {
"port" : "1337"
}
如何取得這個值呢? 就像取得變數方式相同 $npm_package_config_port
同理,用任何的字串當key都可以行得通,
可以先透過 npm run env | grep "npm_package_config"
的方式先行驗證
14. 透過 git hook 執行 npm script
使用 hasky 外掛,可以協助我們在執行 git 指令的時候,觸發 npm 的 script 內容
{
"scripts" : {
"precommit" : "npm test" ,
"prepush" : "npm test" ,
"..." : "..."
}
}
當你做 git 指令操作的時候 git commit -m "Keep calm and commit"
就會觸發 npm test
所有的事件清單
Git hook
npm script
applypatch-msg
applypatchmsg
commit-msg
commitmsg
post-applypatch
postapplypatch
post-checkout
postcheckout
post-commit
postcommit
post-merge
postmerge
post-receive
postreceive
post-rewrite
postrewrite
post-update
postupdate
pre-applypatch
preapplypatch
pre-auto-gc
preautogc
pre-commit
precommit
pre-push
prepush
pre-rebase
prerebase
pre-receive
prereceive
prepare-commit-msg
preparecommitmsg
push-to-checkout
pushtocheckout
update
update
15. 修改 console 輸出的程度
在 npm start
指令後面,可以加上不同的旗標 (flags )。
可以適當的調整 npm script
輸出的訊息內容
-s, --silent: --loglevel silent
-q, --quiet: --loglevel warn
-d: --loglevel info
-dd, --verbose: --loglevel verbose
-ddd: --loglevel silly
更多參數說明
16. 讓 npm script 跨平台更友善
建立一個 window 系統或 linux 或 macos 系統可兼容的 npm script
需要注意到很多細節,並透過相關的套件處理
在 windows 中 NODE_ENV
不是內部或外部命令,也不是可運行的程序,必續透過 npm 套件 cross-env
處理。
安裝完後,直接在 NODE_ENV
前面添加 cross-env
範例:
"build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
在 window 無法混用單引號跟雙引號,必須要用脫逸字元把單引號 ' 改為 \"
例如:
"watch:lint" : "onchange '**/*.js' '**/*.scss' -- npm run lint"
改為
"watch:lint" : "onchange \"**/*.js\" \"**/*.scss\" -- npm run lint"
npm
的環境變數,在windows 也無法直接使用 字號,必須安裝 cross-var
,使用方式與 cross-env
相同
windows 的寫法為
"server:launch": "opn http://localhost:%npm_package_config_port%"
使用 cross-env 之後
"server:launch": "cross-var opn http://localhost:$npm_package_config_port"
把 rm -rf
改為 rimraf
17. 列出 npm script 可用項目與支援 tab 提示功能
透過指令 npm run | less
可以將 專案中的 package.json 裏頭能夠執行 script 列出,另外可以透過安裝 completion
或 ntl
套件 透過 tab 鍵自動提示
設定 bash 支援提示功能
透過 completion
設定 bash
若使用 fishshell
得改用 npm.fish
檔 link 放到 ~/.config/fish/completions/npm.fish
安裝 ntl 套件,透過 ntl 採用詢問的方式執行 script
shell
$ npm install -g ntl
直接在專案資料夾輸入 ntl
即可
18. 添加註解
如果你需要再 package.json
中添加註解,雖然 json
是不能寫註解,但是可以透過一些做法,添加額外的辨識。
例如:
"scripts": {
"//" : "Run, build, and serve"
"start": "#🚀 Run, build, and serve \n\t node index.js",
}
19. 透過 p-s 讓 npm script 寫在 package-scripts.js
中
原生的 package.json
天生有一些限制,我們可透過 p-s
改用 package-scripts.js
格式載入設定。
如此一來就可以在裡頭寫註解或撰寫邏輯程式
$ npm install --global p-s
初始化 package-scripts.js
但裡頭的程式碼就要把 npm 改為 nps ,且 npm-run-all
就不能使用了,也不能使用配通符號,需要改成用逗點的方式串接。例如 "lint":"npm-run-all lint:**",
就要改成 nps lint:test,lint:css,lint:css:fix
20 .透過 bash script 替換複雜的 npm script
可以透過 npm 執行 bash 指令
{ "test" : "./scripts/test.sh" }
記得 test.sh
的檔案標題要設定 bash
#!/usr/bin/env bash
echo "Building..."
rm -rf public/$npm_package_version
echo "Finished"
21.也可以透過 node script 取代 npm script
mkdir scripts
touch scripts/{ test ,build,server} .js
import { exec } from "shelljs" ;
const isWindows = process . platform ;
exec ( ` BABEL_ENV = test mocha spec / -- require babel - register ` );