pnpm 的 shamefully-hoist 選項

近期開始在專案中使用 pnpm 作為套件管理工具,關於 pnpm 的特色及優點不在此贅述, 本篇記錄在其中一項專案使用 pnpm 踩到的雷。

專案 A 依賴的套件 B,而套件 B 依賴了 webpack-dev-server,也就是 A -> B -> webpack-dev-server, 踩到的雷是在 A 當中運行 B 套件的指令時,會出現 webpack-dev-server 找不到的錯誤。

問題其實是 pnpm 的特色所導致,以往在使用 npmyarn 安裝套件時,所有依賴的套件(包括套件所依賴的套件) 會全部被安裝在 node_modules 目錄下,所以即使 webpack-dev-server 沒有被專案「直接依賴」一樣可以被調用到。

pnpm 則是將套件安裝在 node_modules/.pnpm 目錄下, 然後只把專案直接依賴的套件拉升到 node_modules 目錄下(用檔案連結的方式), 所以 node_module 看到的東西很乾淨,只會有專案直接依賴的套件,同樣地 node_modules/.bin 也只會有對應的可執行檔。 這樣的結果就是 webpack-dev-server 被安裝在 node_modules/.pnpm 目錄下,無法被調用到。

方法一:主動聲明 webpack-dev-server 為專案的直接依賴

不傾向這個方法是希望專案能夠保持乾淨,不要有多餘的套件,還有未來需要持續追蹤版本號是否與 B 所依賴的版本號一致的問題。

方法二:使用 shamefully-hoist 選項

--shamefully-hoist 的意思是將所有套件都拉升到 node_modules 目錄下,也就是跟 yarnnpm 相同的行為, 使用方法是在安裝套件時加入此選項,也就是 pnpm install --shamefully-hoist

方法三:使用 public-hoist-pattern

這是我個人偏好的折衷方案,比起 shamefully-hoist 這種直接把所有套件都拉升到 node_modules 目錄, public-hoist-pattern 是一個可以明確指出「只拉升哪些套件」的功能,使用方法是在專案目錄下新增一個 .npmrc 的檔案, 這個檔案是操作 pnpm 指令時會讀取的組態檔案,我們在檔案中新增以下內容以決定要拉升哪些套件:

public-hoist-pattern[]=webpack
public-hoist-pattern[]=webpack-dev-server

設定值支援萬用字元 *,因此上面這個範例也可以寫成 public-hoist-pattern[]=webpack*,設定完後需執行 pnpm install 即可。

參考:Does not install links to binaries of dependencies #3566

留言