Node.js 的 Shell 腳本
你可以購買這本書的離線版本(HTML、PDF、EPUB、MOBI),並支持免費的線上版本。
(廣告,請不要封鎖。)

13 安裝 npm 套件和執行 bin 腳本



package.json 屬性 "bin" 讓 npm 套件可以指定它提供的 Shell 腳本(更多資訊,請參閱 §14「建立跨平台 Shell 腳本」)。如果我們安裝此類套件,Node.js 會確保我們可以從命令列存取這些 Shell 腳本(所謂的bin 腳本)。在本章中,我們將探討兩種安裝具有 bin 腳本的套件的方法

我們將探討所有這些含意的內容,以及如何在安裝 bin 腳本後執行它們。

13.1 全局安裝 npm 登錄檔套件

套件 cowsay 具有下列 package.json 屬性

"bin": {
  "cowsay": "./cli.js",
  "cowthink": "./cli.js"
},

若要全域安裝此套件,我們使用 npm install -g

npm install -g cowsay

注意事項:在 Unix 上,我們可能必須使用 sudo(我們很快就會學到如何避免這樣做)

sudo npm install -g cowsay

在那之後,我們可以在命令列中使用 cowsaycowthink 命令。

請注意,只有 bin 腳本可全域使用。當 Node.js 在 node_modules 目錄中查詢裸模組指定項時,套件會被忽略。

13.1.1 哪些套件已全域安裝?npm ls -g

我們可以檢查哪些套件已全域安裝,以及安裝在哪裡

% npm ls -g
/usr/local/lib
├── corepack@0.12.1
├── cowsay@1.5.0
└── npm@8.15.0

在 Windows 上,安裝路徑為 %AppData%\npm,例如

>echo %AppData%\npm
C:\Users\jane\AppData\Roaming\npm

13.1.2 套件安裝在何處?npm root -g

macOS 上的結果

% npm root -g
/usr/local/lib/node_modules

Windows 上的結果

>npm root -g
C:\Users\jane\AppData\Roaming\npm\node_modules

13.1.3 shell 腳本安裝在何處?npm bin -g

npm bin -g 會告訴我們 npm 在何處全域安裝 shell 腳本。它還會確保 shell PATH 中有該目錄。

macOS 上的結果

% npm bin -g
/usr/local/bin

% which cowsay
/usr/local/bin/cowsay

Windows 命令殼層上的結果

>npm bin -g
C:\Users\jane\AppData\Roaming\npm

>where cowsay
C:\Users\jane\AppData\Roaming\npm\cowsay
C:\Users\jane\AppData\Roaming\npm\cowsay.cmd

沒有檔案副檔名的可執行檔 cowsay 適用於基於 Unix 的 Windows 環境,例如 Cygwin、MinGW 和 MSYS。

Windows PowerShell 會傳回此路徑以供 gcm cowsay

C:\Users\jane\AppData\Roaming\npm\cowsay.ps1

13.1.4 套件安裝在何處?npm 安裝前置詞

npm 的安裝前置詞決定套件和 bin 腳本安裝在何處。

這是 macOS 上的安裝前置詞

% npm config get prefix
/usr/local

因此

這是 Windows 上的安裝前置詞

>npm config get prefix
C:\Users\jane\AppData\Roaming\npm

因此

13.1.5 變更套件的全域安裝位置

在本節中,我們將探討變更套件全域安裝位置的兩種方法

13.1.5.1 變更 npm 安裝前置詞

變更套件全域安裝位置的一種方法是變更 npm 安裝前置詞。

Unix

mkdir ~/npm-global
npm config set prefix '~/npm-global'

Windows 命令殼層

mkdir "%UserProfile%\npm-global"
npm config set prefix "%UserProfile%\npm-global"

Windows PowerShell

mkdir "$env:UserProfile\npm-global"
npm config set prefix "$env:UserProfile\npm-global"

組態資料會儲存到主目錄中的 .npmrc 檔案。

從現在開始,全域安裝會新增到我們剛剛指定的目錄中。

之後,我們仍然必須將 npm bin -g 目錄新增到我們的 shell PATH,這樣我們的 shell 才能找到我們全域安裝的 bin 腳本。

變更 npm 前綴的缺點:如果我們告訴 npm 自行升級,它現在也會安裝在新位置。

13.1.5.2 使用 Node.js 版本管理員

Node.js 版本管理員讓我們可以同時安裝多個版本的 Node.js,並在它們之間切換。熱門的版本管理員包括

13.2 在本地安裝 npm 登錄檔套件

若要在本地(安裝到套件中)安裝 npm 登錄檔套件,例如 cowsay,我們會執行下列動作

cd my-package/
npm install cowsay

這會將下列資料新增到 package.json

"dependencies": {
  "cowsay": "^1.5.0",
  ···
}

此外,套件會下載到下列目錄

my-package/node_modules/cowsay/

在 Unix 上,npm 會為 bin 腳本新增這些符號連結

my-package/node_modules/.bin/cowsay -> ../cowsay/cli.js
my-package/node_modules/.bin/cowthink -> ../cowsay/cli.js

在 Windows 上,npm 會將這些檔案新增到 my-package\node_modules\.bin\

cowsay
cowsay.cmd
cowsay.ps1
cowthink
cowthink.cmd
cowthink.ps1

沒有副檔名的檔案是針對 Unix-based Windows 環境(例如 Cygwin、MinGW 和 MSYS)的腳本。

npm bin 會告訴我們在本地安裝的 bin 腳本位於何處,例如

% npm bin
/Users/john/my-package/node_modules/.bin

注意:在本地,套件總是安裝在 package.json 檔案旁邊的 node_modules 目錄中。如果目前目錄中不存在後者,npm 會在祖先目錄中搜尋它,並在那裡安裝套件。若要查看 npm 會在何處在本地安裝套件,我們可以使用指令 npm root,例如(Unix)

% cd $HOME
% npm root
/Users/john/node_modules

John 的家目錄中沒有 package.json,但 npm 無法在祖先目錄中安裝任何東西,這就是 npm root 顯示這個目錄的原因。在目前位置在本地安裝套件會導致建立 package.json,並照常進行安裝。

13.2.1 執行在本地安裝的 bin 腳本

(本小節中的所有指令都在 my-package 目錄中執行。)

13.2.1.1 直接執行 bin 腳本

我們可以從 shell 如下執行 cowsay

./node_modules/.bin/cowsay Hello

在 Unix 上,我們可以設定一個輔助程式

alias npm-exec='PATH=$(npm bin):$PATH'

然後,下列指令會起作用

npm-exec cowsay Hello
13.2.1.2 透過套件腳本執行 bin 腳本

我們也可以將套件腳本新增到 package.json

{
  ···
  "scripts": {
    "cowsay": "cowsay"
  },
  ···
}

現在,我們可以在 shell 中執行這個指令

npm run cowsay Hello

這會起作用,因為 npm 會暫時將下列項目新增到 Unix 上的 $PATH

/Users/john/my-package/node_modules/.bin
/Users/john/node_modules/.bin
/Users/node_modules/.bin
/node_modules/.bin

在 Windows 上,類似的項目會新增到 %Path%$env:Path

C:\Users\jane\my-package\node_modules\.bin
C:\Users\jane\node_modules\.bin
C:\Users\node_modules\.bin
C:\node_modules\.bin

下列指令會列出套件腳本執行期間存在的環境變數及其值

npm run env
13.2.1.3 透過 npx 執行 bin 腳本

在套件中,可以使用 npx 存取 bin 腳本

npx cowsay Hello
npx cowthink Hello

稍後會進一步說明 npx。

13.3 安裝未發布的套件

有時,我們有一個尚未發布或永遠不會發布的套件,而且想要安裝它。

假設我們有一個未發佈的套件,其名稱為 @my-scope/unpublished-package,儲存在目錄 /tmp/unpublished-package/ 中。我們可以透過以下方式讓它在全域中可用

cd /tmp/unpublished-package/
npm link

如果我們這樣做

由於連結套件的參照方式,套件中的任何變更都會立即生效。變更時無需重新連結。

若要檢查全域安裝是否成功,我們可以使用 npm ls -g 列出所有已安裝的全域套件。

在我們將未發佈的套件安裝至全域後(請參閱前一個小節),我們可以選擇將它安裝至我們的其中一個套件(可以是已發佈或未發佈)

cd /tmp/other-package/
npm link @my-scope/unpublished-package

這會建立以下連結

/tmp/other-package/node_modules/@my-scope/unpublished-package
-> ../../../unpublished-package

預設情況下,未發佈的套件不會新增為 package.json 的相依性。其背後的原因是 npm link 通常用於暫時使用註冊表套件的未發佈版本——這不應顯示在相依性中。

取消本機連結

cd /tmp/other-package/
npm uninstall @my-scope/unpublished-package

取消全域連結

cd /tmp/unpublished-package/
npm uninstall -g

13.3.4 透過本機路徑安裝未發佈的套件

安裝未發佈套件至本機的另一種方法是使用 npm install 並透過本機路徑(而非套件名稱)參照它

cd /tmp/other-package/
npm install ../unpublished-package

這有兩個影響。

首先,會建立以下符號連結

/tmp/other-package/node_modules/@my-scope/unpublished-package
-> ../../../unpublished-package

其次,會新增一個相依性至 package.json

"dependencies": {
  "@my-scope/unpublished-package": "file:../unpublished-package",
  ···
}

這種安裝未發佈套件的方法也適用於全域

cd /tmp/unpublished-package/
npm install -g .

13.3.5 安裝未發佈套件的其他方法

13.4 npx:在未安裝 npm 套件的情況下執行 bin 腳本

npx 是與 npm 捆綁在一起的 shell 命令,用於執行 bin 腳本。

它最常見的用法是

npx <package-name> arg1 arg2 ...

此命令會在 npx 快取中安裝名稱為 package-name 的套件,並執行與該套件同名的 bin 腳本,例如

npx cowsay Hello

這表示我們可以在不先安裝 bin 腳本的情況下執行它們。npx 最適合一次性呼叫 bin 腳本,例如,許多架構都提供 bin 腳本來設定新專案,而這些腳本通常會透過 npx 執行。

npx 第一次使用套件後,該套件就會出現在其快取中,後續呼叫的速度也會快很多。不過,我們無法確定套件會在快取中停留多久。因此,npx 無法取代全球或本地安裝 bin 腳本。

如果套件附帶的名稱與其套件名稱不同的 bin 腳本,我們可以像這樣存取它們

npx --package=<package-name> <bin-script> arg1 arg2 ...

例如

npx --package=cowsay cowthink Hello

13.4.1 npx 快取

npx 的快取位在哪裡?

在 Unix 中,我們可以透過以下命令找出

npx --package=cowsay node -p \
  "process.env.PATH.split(':').find(p => p.includes('_npx'))"

它會傳回類似以下的的路徑

/Users/john/.npm/_npx/8f497369b2d6166e/node_modules/.bin

在 Windows 中,我們可以使用 (將一行分成兩行)

npx --package=cowsay node -p
  "process.env.Path.split(';').find(p => p.includes('_npx'))"

它會傳回類似以下的路徑 (將單一路徑分成兩行)

C:\Users\jane\AppData\Local\npm-cache\_npx\
  8f497369b2d6166e\node_modules\.bin

請注意,npx 的快取與 npm 用於安裝模組的快取不同

兩個快取的父目錄可以透過以下方式確定

npm config get cache

有關 npm 快取的更多資訊,請參閱 npm 文件

與 npx 快取相反,資料永遠不會從 npm 快取中移除,只會新增。我們可以在 Unix 上透過以下方式檢查其大小

du -sh $(npm config get cache)/_cacache/

在 Windows PowerShell 上

DiskUsage /d:0 "$(npm config get cache)\_cacache"