如何解決 npm 包須要強制安裝其依賴包的最低版本?javascript
最近公司在自研一套小程序跨端的框架,遇到一個小問題 —— npm 包須要強制安裝其依賴包的最低版本。簡單來講,是這樣的:vue
node_module
裝了一個包,專門負責編譯過程,名爲 compile
node_module
還裝了一個包,是放組件庫的,名爲 components
如今 compile
npm 的包版本升級到 0.2.0
, 須要 components
的npm 包版本配合升級到 1.3.0
。 緣由多是compile
npm 的包修改了某個編譯規則,組件庫裏的某種寫法須要改變,不然會報錯。java
問題是,公司有多條業務線,存在多個工程,不可能每次 compile
包升級後,我都人肉去升級每一個工程的 components
包,不然要被業務同窗懟死。node
這個問題,vue
也遇到過。在 vue 官網有那麼這句話,以下:git
每一個
vue
包的新版本發佈時,一個相應版本的vue-template-compiler
也會隨之發佈。編譯器的版本必須和基本的vue
包保持同步,這樣vue-loader
就會生成兼容運行時的代碼。這意味着你每次升級項目中的vue
包時,也應該匹配升級vue-template-compiler
。npm
因此,問題的本質是,如何保證 依賴包之間的強制最低版本要求json
在咱們工程中,每次開發者啓動項目,會有一個小彩蛋,咱們會友好熱情的舒適提示開發者,今天的天氣如何如何。其實本質上,就是啓動的時候,去服務端拉取一個動態腳本去執行。小程序
咱們想到了,能夠在這個環節,作一點事情。好比說,檢查一下用戶安裝的版本是否ok。框架
在服務端,咱們維護一份配置文件,這份配置文件描述了依賴包之間的強制最低版本要求。ui
服務端能夠本身搭建的一個node服務,甚至,最簡單的作法,也能夠放在一個git倉庫裏面。
每次開發者執行 npm run dev
的時候,也就是在 pre
這個鉤子時候,就去服務端拉取這份配置文件,而後,檢查一下用戶本地安裝的版本是否知足配置文件中的描述。
可是,這種方案存在一個問題,就是不太好維護。
首先,每次我發佈一個新的 compile
包,版本都更新一次,那麼這份配置文件也須要更新一次。
其次,配置文件長這個樣子:
{
"compile編譯包": {
"0.0.1版本": {
"A依賴包": "0.1.2",
"B依賴包": "0.1.3",
"c依賴包": "0.1.3",
},
"0.0.2版本": {
"A依賴包": "0.1.2",
"B依賴包": "0.1.4",
"c依賴包": "0.1.5",
}
}
}
複製代碼
由於這份配置文件是公用的。每次 compile編譯包
升級,都要 新加上一段代碼,日積月累,這個配置就會變得很大很大。
因此,最終咱們選擇了第二種方案。
事實上,vue 等框架也是這樣作的。
直接把強制依賴的版本校驗的邏輯,寫死在代碼裏面。
在初始化以前,就去校驗自身版本,和依賴版本,是否符合配置文件的描述。配置文件是放在包內,隨版本走的。
下面是代碼的具體實現:
下面定義了 compare
方法,用於比較 "0.2.3"
, 和 "0.3.4"
兩個字符串,表示的版本號大小
let _isVersion = (v) => {
let result = /^\d+(\.\d+)*$/.test(v);
return result;
};
let compare = (v1, v2) => {
if (_isVersion(v1) && _isVersion(v2)) {
v1 = v1.toString().split('.');
v2 = v2.toString().split('.');
for (let i = 0, l1 = v1.length, l2 = v2.length; i < l1 || i < l2; i++) {
let n1 = parseInt(v1[i], 10);
let n2 = parseInt(v2[i], 10);
if (n1 < n2) {
return -1;
} else if (n1 > n2) {
return 1;
}
}
return 0;
}
return console.error('version value is invalid');
};
複製代碼
下面定義了 配置文件config.js
// compiler 依賴包最低版本的配置
module.exports = {
'A依賴包': {
'max': '',
'min': '0.1.2',
'require': true
}
}
複製代碼
下面定義了 _checkDependencyVersion
方法:
// 檢查依賴包的版本
const _checkDependencyVersion = () => {
return new Promise((resolve, reject) => {
let depMinOption = require('./config.js');
for (let key in depMinOption) {
let dep = depMinOption[key];
try {
let depSource = require(`${key}/package.json`);
let sourceVersion = depSource.version;
if (dep.min && compare(sourceVersion, dep.min) === -1) {
console.log(` |***************************************************** | | ${chalk.red('ERROR!')} | ${chalk.red(`toolkit 依賴 ${key} 的最低版本是 ${dep.min}, 請升級 ${key} 到 ${dep.min} 及以上`)} | |***************************************************** `);
resolve(false);
}
if (dep.max && compare(sourceVersion, dep.max) === 1) {
console.log(` |***************************************************** | | ${chalk.red('ERROR!')} | ${chalk.red(`toolkit 依賴 ${key} 的最高版本是 ${dep.max}, 請調整 ${key} 版本`)} | |***************************************************** `);
resolve(false);
}
resolve(true)
} catch (e) {
console.log(e)
if (dep.require) {
console.log(` |***************************************************** | | ${chalk.red('ERROR!')} | ${chalk.red(`toolkit 依賴 ${key} ${dep.min ? '最低版本:' + dep.min: ''} ${dep.max ? '最高版本:' + dep.max: ''}`)} | |***************************************************** `);
}
}
}
})
}
複製代碼
上面的 _checkDependencyVersion
方法會在 npm run dev
以後立刻執行,若是發現,開發者本地裝的版本不ok, 則會提醒開發者升級對應包的版本。
若是有更好的解決方案,歡迎在下方評論區留言。