npm依赖包版本号固定方法

为什么固定版本号?

  1. 为了安全。新版本有可能被黑客植入病毒。
    如何看待 NPM 包 event-stream 被黑客篡改,发现包含恶意代码?

  2. 保证功能一致性。
    一般情况下,限定了major和minor版本,Library对外暴露的API是向下兼容的,但有时候作者会修复Bug或者添加新功能,很可能影响原有功能的一致性,甚至产生新的Bug。

  3. 降低维护成本。
    虽然有package-lock.json这个文件,可以显式地锁定依赖包的版本,但它本身是不可靠的,且易变。
    一旦Library的作者发布了新版本,或者项目开发者自己使用了npm update命令,package-lock.json又会发生变化。
    如果我们依赖这种包管理的方式,在看代码PR时,还要观察这个文件的变更,成本太高。

 

有风险的做法

有几种写法?分别代表什么意思?看一下官方的介绍 https://docs.npmjs.com/files/package.json#dependencies

以下版本号写法,都是有风险的:

  1. 插入符号(^),限定major版本:
    功能兼容,旧的API不会消失,但可能会有新的API。
    如"@koa/cors": "^2.2.3",
    实际安装的版本会是 version >=2.2.3 && version < 3.0.0

  2. 波浪符号(~),限定minor版本:
    功能几乎一样,API不变,可能会有小问题修复和改进。
    如"@koa/cors": "~2.2.3",
    实际安装的版本会是 version >=2.2.3 && version < 2.3.0

解决办法

可以用以下命令查看安装的依赖:

npm list --depth=0

以下代码可以检查当前安装的包的版本,并固化版本号。如果对脚本不放心,那就用npm list 命令吧。

自动修改package.json,会通过npm list --depth=0,找到本地实际安装的依赖包版本号,替换掉package.json中的范围版本号。

const process = require('child_process');
const fs = require('fs');
const path = require('path');
const packageFilePath = path.resolve('package.json');
let packageContentStr = fs.readFileSync(packageFilePath, 'utf-8');

function fixPackagesVersion() {
  const packageContent = JSON.parse(packageContentStr);
  replaceDepsVersion(packageContent.dependencies);
  replaceDepsVersion(packageContent.devDependencies);
}

function replaceDepsVersion(dependencies) {
  Object.keys(dependencies).forEach(function (packageName) {
    const originalVerStr = `"${packageName}"\\s*:\\s*".*"`;
    const packageStartReg = new RegExp(originalVerStr);
    const finalVer = getFinalVersion(packageName);
    if (finalVer) {
      const stableVersionStr = `"${packageName}": "${finalVer}"`;
      packageContentStr = packageContentStr.replace(packageStartReg, stableVersionStr);
    }
  });
}

function getFinalVersion(packageName) {
  const str = actualInstalledPackages;
  const regStr = `──\\s${packageName}@.*`;
  const reg = new RegExp(regStr);
  const packageAndVerResult = str.match(reg);
  if (packageAndVerResult && packageAndVerResult.length) {
    const value = packageAndVerResult[0];
    return value.substring(value.lastIndexOf('@')+1);
  } else {
    return '';
  }
}



const command = 'npm list --depth=0';
let actualInstalledPackages;
process.exec(command, (err, stdout, stderr) => {
  actualInstalledPackages = stdout;
  fixPackagesVersion();
  fs.writeFileSync(packageFilePath, packageContentStr, 'utf-8');
});

 

你可能感兴趣的:(npm依赖包版本号固定方法)