記node_modules版本鎖定方案的落地
背景
半年前開始關注 npm shrinkwrap
,因為專案裡每次 npm install
都會出現因依賴庫版本不一致導致的構建問題。
當時的預設安裝工具是 tnpm,由於 tnpm 從 cnpm 來,cnpm 又通過 npminstall 實現,而 npminstall 又不支援 shrinkwrap
,無奈只能考慮通過 npm 方式進行安裝。
官方 npm 沒有 @ali
依賴,必須使用 --registry=http://registry.npm.alibaba-inc.com
指定資源倉庫。
對於一般的專案,設定一個別名即可解決這個問題:
alias alinpm=`npm --registry=http://registry.npm.alibaba-inc.com`
# 或 cnpm
alias alinpm=`npm --registry=https://registry.npm.taobao.org`
但問題又來了,本地專案中有一模組 @ali/imagemin
依賴以下這些模組:
- advpng-bin
- jpeg-recompress-bin
- jpegtran-bin
- optipng-bin
- pngcrush-bin
- pngquant-bin
這些模組安裝時會從 github 下載執行檔案,具體流程如下:
因為有兩處邏輯涉及下載,而下載地址又是 github cdn。國內環境不出意外的話一定被牆,所以 npm 安裝方式行不通,安裝到 pngquant 時會報 pngquant pre-build test failed
。之後走原始碼構建邏輯又從 github 下載,繼續報 pngquant failed to build
:
但奇怪的是 tnpm 安裝卻一切正常:
為什麼
非常有意思,扒扒 npminstall 原始碼跟蹤安裝流程,發現 bin/install.js
指令碼中有一段:
// if in china, will automatic using chines registry and mirros.
const inChina = argv.china || !!process.env.npm_china;
// if exists, override default china mirror url
const customChinaMirrorUrl = argv[`custom-china-mirror-url`];
順著 customChinaMirrorUrl
找到了:
這段程式碼表示從這幾處資源倉庫裡找 binary-mirror-config
模組的最新版本,下載後返回mirrors.china
。搜一下,發現 npminstall 用了一個比較雞賊的辦法:
case by case 的把所有需要下載的二進位制全做了一次映象!
順藤摸瓜
抑制不住興奮繼續往下扒,看看在哪裡做了處理:
yield installLocal(config);
+ require(`./local_install`)
+ _install()
+ installOne()
+ install()
+ _install()
+ download()
+ npm()
+ download()
終於在 lib/download/npm.js
的第 238 行看到了 pngquant-bin
:
// use mirror url instead
// e.g.: pngquant-bin
const indexFilepath = path.join(ungzipDir, `lib/index.js`);
yield replaceHostInFile(pkg, indexFilepath, binaryMirror, options);
const installFilepath = path.join(ungzipDir, `lib/install.js`);
yield replaceHostInFile(pkg, installFilepath, binaryMirror, options);
npminstall 在下載流程裡單獨處理了所有需要映象的二進位制執行檔案,找到一處匹配就 replace 其 binary host,起到映象的效果。
解決它
找到關鍵點就好辦了,目前有兩種方案:
- 單獨為每個模組的 package.json 新增 postinstall 指令碼
- 通過 npm 鉤子
hooks script
對所有模組單獨進行處理
修改別人的模組顯然不可能,那就只能用方案2了
編寫指令碼
要使 hooks script
起作用,得在 node_modules 目錄裡建立一個 .hooks 目錄,裡面存放著以「事件名稱」命名的指令碼檔案(安裝指令碼請參見:npm scripts)
project_dir
+ node_modules
+ .hooks
+ preinstall <---
在 preinstall 指令碼里可以使用 process.env.npm_package_name
獲得當前安裝的模組名稱,虛擬碼如下:
#!/usr/bin/env node
if(`pngquant-bin` === process.env.npm_package_name){
const PWD = process.env.PWD;
replaceHostInFile(path.join(PWD, `lib/index.js`));
replaceHostInFile(path.join(PWD, `lib/install.js`));
}
function replaceHostInFile(filepath) {
const exists = fs.existsSync(filepath);
if (!exists) return;
let content = fs.readFileSync(filepath, `utf8`);
content = content.replace(///raw.github.com/, `//raw.github.cnpmjs.org`);
content = content.replace(///github.com/, `//github.com.cnpmjs.org`);
fs.writeFileSync(filepath, content);
}
執行結果:
到此,問題已完全解決,安裝上最新的 npm 5.x ,輕鬆使用 package-lock.json
提供的版本鎖定特性。
更進一步
實際使用中不可能每個專案都複製一份 hooks scripts,要脫離 case by case 必須自動化。
不細講,以下是最終版本,歡迎大家試用:
用法
- 全域性安裝
hook-binary-mirror
模組
# tnpm 方式
tnpm --by=npm i hook-binary-mirror -g
# npm 方式
npm --registry=https://registry.npm.taobao.org i hook-binary-mirror -g
- 刪除原有的 node_modules 目錄
cd project_dir
rm -rf node_modules
- 為 package.json 增加一處 scripts
"scripts": {
"preinstall": "hook-binary-mirror"
}
完成
結束
困擾了半年多的問題終於解決,此項任務終於可以放心的置為「已結束」。過程中翻閱了 npminstall 的原始碼,獲益良多。
相關文章
- 鎖定專案的 node 版本
- SAP BPS : Exit Function 實現版本鎖定Function
- JavaScript專案中鎖定npm依賴包版本JavaScriptNPM
- 鎖定專案 Node 版本和包管理器
- 論《資料落地》的方案總結
- 如何快速鎖定 Mac,Macbook快速鎖定的幾種方法Mac
- 如何在其他Session跳過被鎖定的記錄Session
- 談談秒殺系統的落地方案
- 分散式鎖的解決方案分散式
- 分散式鎖的實現方案分散式
- Rock Pi開發筆記(一):Rock Pi系列arm產品方案快速落地方案介紹筆記
- 如何修改 node_modules 裡的檔案
- 鎖定、解鎖Qtum錢包QT
- 資料治理方案落地的5步必經之路
- 【分散式鎖的演化】常用鎖的種類以及解決方案分散式
- Flutter 鎖定行列的 FlexGridFlutterFlex
- 悲觀鎖定的應用
- Linux記憶體子系統——Locking Pages(記憶體鎖定)Linux記憶體
- 最好的免費開源供應鏈安全工具:鎖定檔案版本 - r2c
- 智聯招聘的Web模組擴充套件落地方案Web套件
- 記錄一下MySql update會鎖定哪些範圍的資料MySql
- 鎖的總結筆記筆記
- ORACLE ERP解決死鎖的方案Oracle
- excel鎖定公式$怎麼輸入 表格中如何鎖定公式Excel公式
- MySQL – 事務的啟動 / 設定 / 鎖 / 解鎖——入門MySql
- 處理表鎖定的情況
- SQL Server 中的鎖定介紹SQLServer
- 鎖--筆記筆記
- Enqueue 鎖定機制ENQ
- 表鎖定,kill sessionSession
- 落地即王道,鎖死企業智變CP——雲+AIAI
- promise時效架構升級方案的實施及落地Promise架構
- jvm記憶體設定及記憶體溢位、解決方案JVM記憶體溢位
- Oracle報錯>記錄被另外一個使用者鎖定Oracle
- win10輸入法鎖定怎麼設定_win10輸入法鎖定的具體方法Win10
- win10如何設定自動鎖定螢幕_win10設定自動鎖屏的步驟Win10
- win10指紋鎖如何設定_win10設定指紋解鎖的方法Win10
- 如何優雅的修改node_modules中的依賴庫